Back to index

lightning-sunbird  0.9+nobinonly
nsWebScriptsAccess.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the web scripts access security code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s): Harish Dhurvasula <harishd@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsWebScriptsAccess.h"
00039 #include "nsString.h"
00040 #include "nsAutoPtr.h"
00041 #include "nsIDOMDocument.h"
00042 #include "nsIDOMElement.h"
00043 #include "nsIDOMNodeList.h"
00044 #include "nsIDOMAttr.h"
00045 #include "nsIDOMNamedNodeMap.h"
00046 #include "nsIPrincipal.h"
00047 #include "nsIURL.h"
00048 #include "nsReadableUtils.h"
00049 #include "nsIHttpChannel.h"
00050 #include "nsNetUtil.h"
00051 #include "nsIXPConnect.h"
00052 #include "jsapi.h"
00053 
00054 #include "nsISOAPCall.h"
00055 #include "nsISOAPEncoding.h"
00056 #include "nsISOAPResponse.h"
00057 #include "nsISOAPFault.h"
00058 #include "nsISOAPParameter.h"
00059 #include "nsISOAPBlock.h"
00060 #include "nsIVariant.h"
00061 #include "nsIPrefService.h"
00062 #include "nsIPrefBranch2.h"
00063 #include "nsIJSContextStack.h"
00064 
00065 #define WSA_GRANT_ACCESS_TO_ALL     (1 << 0)
00066 #define WSA_FILE_NOT_FOUND          (1 << 1)
00067 #define WSA_FILE_DELEGATED          (1 << 2)
00068 #define SERVICE_LISTED_PUBLIC       (1 << 3)
00069 #define HAS_MASTER_SERVICE_DECISION (1 << 4)
00070 
00071 static PRBool PR_CALLBACK 
00072 FreeEntries(nsHashKey *aKey, void *aData, void* aClosure)
00073 {
00074   AccessInfoEntry* entry = NS_REINTERPRET_CAST(AccessInfoEntry*, aData);
00075   delete entry;
00076   return PR_TRUE;
00077 }
00078 
00079 NS_IMPL_ISUPPORTS1(nsWebScriptsAccess, 
00080                    nsIWebScriptsAccessService)
00081 
00082 nsWebScriptsAccess::nsWebScriptsAccess()
00083   : NS_LITERAL_STRING_INIT(kNamespace2002, "http://www.mozilla.org/2002/soap/security")
00084   , NS_LITERAL_STRING_INIT(kWebScriptAccessTag, "webScriptAccess")
00085   , NS_LITERAL_STRING_INIT(kDelegateTag, "delegate")
00086   , NS_LITERAL_STRING_INIT(kAllowTag, "allow")
00087   , NS_LITERAL_STRING_INIT(kTypeAttr, "type")
00088   , NS_LITERAL_STRING_INIT(kFromAttr, "from")
00089   , NS_LITERAL_STRING_INIT(kAny, "any")
00090   , NS_LITERAL_STRING_INIT(kIsServicePublic, "isServicePublic")
00091 {
00092 }
00093 
00094 nsWebScriptsAccess::~nsWebScriptsAccess()
00095 {
00096   mAccessInfoTable.Enumerate(FreeEntries, this);
00097 }
00098 
00099 NS_IMETHODIMP 
00100 nsWebScriptsAccess::CanAccess(nsIURI* aTransportURI,
00101                               const nsAString& aRequestType,
00102                               PRBool* aAccessGranted)
00103 {
00104   *aAccessGranted = PR_FALSE;
00105   NS_ENSURE_ARG_POINTER(aTransportURI);
00106 
00107   nsresult rv;
00108   if (!mSecurityManager) {
00109     mSecurityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00110     NS_ENSURE_SUCCESS(rv, rv);
00111   }
00112    
00113   rv =
00114     mSecurityManager->IsCapabilityEnabled("UniversalBrowserRead", 
00115                                           aAccessGranted);
00116   if (NS_FAILED(rv) || *aAccessGranted)
00117     return rv;
00118   
00119   mServiceURI = aTransportURI;
00120 
00121   nsXPIDLCString path;
00122   aTransportURI->GetPrePath(path);
00123   path += '/';
00124 
00125   AccessInfoEntry* entry = 0;
00126   rv = GetAccessInfoEntry(path, &entry);
00127   if (!entry) {
00128     rv = mSecurityManager->CheckSameOrigin(0, aTransportURI);
00129     if (NS_SUCCEEDED(rv)) {
00130       // script security manager has granted access
00131       *aAccessGranted = PR_TRUE;
00132       return rv;
00133     }
00134     else {
00135       // Script security manager has denied access and has set an
00136       // exception. Clear the exception and fall back on the new
00137       // security model's decision.
00138       nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
00139       if (xpc) {
00140         nsCOMPtr<nsIXPCNativeCallContext> cc;
00141         xpc->GetCurrentNativeCallContext(getter_AddRefs(cc));
00142         if (cc) {
00143           JSContext* cx;
00144           rv = cc->GetJSContext(&cx);
00145           NS_ENSURE_SUCCESS(rv, rv);
00146 
00147           JS_ClearPendingException(cx);
00148           cc->SetExceptionWasThrown(PR_FALSE);
00149         }
00150       }
00151     }
00152 
00153     rv = CreateEntry(path, PR_FALSE, &entry);
00154     NS_ENSURE_SUCCESS(rv, rv);
00155   }
00156 
00157   return CheckAccess(entry, aRequestType, aAccessGranted);
00158 }
00159 
00160 NS_IMETHODIMP 
00161 nsWebScriptsAccess::InvalidateCache(const char* aTransportURI)
00162 {
00163   if (aTransportURI) {
00164     nsCStringKey key(aTransportURI);
00165     if (mAccessInfoTable.Exists(&key)) {
00166       AccessInfoEntry* entry = 
00167         NS_REINTERPRET_CAST(AccessInfoEntry*, mAccessInfoTable.Remove(&key));
00168       delete entry;
00169     }
00170   }
00171   else {
00172     // If a URI is not specified then we clear the entire cache.
00173     mAccessInfoTable.Enumerate(FreeEntries, this);
00174   }
00175   return NS_OK;
00176 }
00177 
00178 nsresult 
00179 nsWebScriptsAccess::GetAccessInfoEntry(const char* aKey,
00180                                        AccessInfoEntry** aEntry)
00181 {
00182   nsCStringKey key(aKey);
00183 
00184   *aEntry = NS_REINTERPRET_CAST(AccessInfoEntry*, mAccessInfoTable.Get(&key));
00185   if (*aEntry  && ((*aEntry)->mFlags & WSA_FILE_DELEGATED)) {
00186     nsresult rv;
00187     nsCOMPtr<nsIURL> url(do_QueryInterface(mServiceURI, &rv));
00188     NS_ENSURE_SUCCESS(rv, rv);
00189   
00190     nsCAutoString path;
00191     url->GetPrePath(path);
00192     nsCAutoString directory;
00193     url->GetDirectory(directory);
00194     path += directory;
00195 
00196     return GetAccessInfoEntry(path.get(), aEntry);
00197   }
00198   return NS_OK;
00199 }
00200 
00201 nsresult 
00202 nsWebScriptsAccess::GetDocument(const nsACString& aDeclFilePath,
00203                                 nsIDOMDocument** aDocument)
00204 {
00205   nsresult rv = NS_OK;
00206   
00207   if (!mRequest) {
00208     mRequest = do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
00209     NS_ENSURE_SUCCESS(rv, rv);
00210   }
00211 
00212   const nsAString& empty = EmptyString();
00213   rv = mRequest->OpenRequest(NS_LITERAL_CSTRING("GET"), aDeclFilePath,
00214                              PR_FALSE, empty, empty);
00215   NS_ENSURE_SUCCESS(rv, rv);
00216     
00217   rv = mRequest->OverrideMimeType(NS_LITERAL_CSTRING("application/xml"));
00218   NS_ENSURE_SUCCESS(rv, rv);
00219 
00220   rv = mRequest->Send(0);
00221   NS_ENSURE_SUCCESS(rv, rv);
00222 
00223   nsCOMPtr<nsIChannel> channel;
00224   mRequest->GetChannel(getter_AddRefs(channel));
00225   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel, &rv));
00226   NS_ENSURE_TRUE(httpChannel, rv);
00227 
00228   PRBool succeeded;
00229   httpChannel->GetRequestSucceeded(&succeeded);
00230  
00231   if (succeeded) {
00232     rv = mRequest->GetResponseXML(aDocument);
00233     NS_ENSURE_SUCCESS(rv, rv);
00234   }
00235 
00236   return rv;
00237 }
00238 
00239 nsresult
00240 nsWebScriptsAccess::GetCodebaseURI(nsIURI** aCodebase)
00241 {
00242   nsresult rv = NS_OK;
00243  
00244   if (!mSecurityManager) {
00245     mSecurityManager = 
00246       do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00247     NS_ENSURE_SUCCESS(rv, rv);
00248   }
00249 
00250   nsCOMPtr<nsIPrincipal> principal;
00251   rv = mSecurityManager->GetSubjectPrincipal(getter_AddRefs(principal));
00252   NS_ENSURE_SUCCESS(rv, rv);
00253   
00254   return principal->GetURI(aCodebase);
00255 }
00256 
00257 nsresult 
00258 nsWebScriptsAccess::CreateEntry(const char* aKey,
00259                                 const PRBool aIsDelegated,
00260                                 AccessInfoEntry** aEntry)
00261 {
00262   NS_ENSURE_ARG_POINTER(aEntry);
00263   *aEntry = nsnull;
00264   // create an entry by loading the declaration file (
00265   // web-scripts-access.xml ) and extracting access information from
00266   // it. Record the extracted info. for this session
00267   nsCOMPtr<nsIDOMDocument> document;
00268   nsresult rv = 
00269     GetDocument(nsDependentCString(aKey) +
00270                 NS_LITERAL_CSTRING("web-scripts-access.xml"),
00271                 getter_AddRefs(document));
00272   NS_ENSURE_SUCCESS(rv, rv);
00273   if (document) {
00274     // Create an entry by extracting access information from the document.
00275     rv = CreateEntry(document, aIsDelegated, aEntry);
00276     NS_ENSURE_SUCCESS(rv, rv);
00277 
00278     // If the document is invalid then an entry will not be created.
00279     if (!*aEntry)
00280       return NS_OK;
00281   }
00282   else {
00283     rv = CreateEntry(WSA_FILE_NOT_FOUND, aEntry);
00284     NS_ENSURE_SUCCESS(rv, rv);
00285   }
00286   nsCStringKey key(aKey);
00287   mAccessInfoTable.Put(&key, *aEntry);
00288 
00289   NS_ASSERTION(*aEntry, "unexpected: access info entry is null!");
00290   if (*aEntry  && ((*aEntry)->mFlags & WSA_FILE_DELEGATED))
00291     rv = CreateDelegatedEntry(aEntry);
00292   return rv;
00293 }
00294 
00295 nsresult 
00296 nsWebScriptsAccess::CreateEntry(nsIDOMDocument* aDocument,
00297                                 const PRBool aIsDelegated,
00298                                 AccessInfoEntry** aEntry)
00299 {
00300   NS_ENSURE_ARG_POINTER(aDocument);
00301   NS_ENSURE_ARG_POINTER(aEntry);
00302   *aEntry = nsnull;
00303   
00304   PRBool valid;
00305   nsresult rv = ValidateDocument(aDocument, &valid);
00306   NS_ENSURE_SUCCESS(rv, rv);
00307 
00308   if (!valid) {
00309     return NS_OK; // XXX should I return an error instead ?
00310   }
00311 
00312   if (!aIsDelegated) {
00313     nsCOMPtr<nsIDOMNodeList> delegateList; 
00314     rv = aDocument->GetElementsByTagNameNS(kNamespace2002, kDelegateTag, 
00315                                            getter_AddRefs(delegateList));
00316     NS_ENSURE_TRUE(delegateList, rv);
00317     nsCOMPtr<nsIDOMNode> node;
00318     delegateList->Item(0, getter_AddRefs(node));
00319     if (node)
00320       return CreateEntry(WSA_FILE_DELEGATED, aEntry);
00321   }
00322 
00323   nsCOMPtr<nsIDOMNodeList> allowList;
00324   rv = aDocument->GetElementsByTagNameNS(kNamespace2002, kAllowTag, 
00325                                          getter_AddRefs(allowList));
00326   NS_ENSURE_TRUE(allowList, rv);
00327 
00328   PRUint32 count;
00329   allowList->GetLength(&count);
00330   if (count) {
00331     rv = CreateEntry(allowList, aEntry);
00332   }
00333   else {
00334     // Since there are no ALLOW elements present grant access to all.
00335     rv = CreateEntry(WSA_GRANT_ACCESS_TO_ALL, aEntry);
00336   }
00337 
00338   return rv;
00339 }
00340 
00341 nsresult
00342 nsWebScriptsAccess::CreateEntry(const PRInt32 aFlags, 
00343                                 AccessInfoEntry** aEntry)
00344 {
00345   *aEntry = new AccessInfoEntry(aFlags);
00346   NS_ENSURE_TRUE(*aEntry, NS_ERROR_OUT_OF_MEMORY);
00347 
00348   return NS_OK;
00349 }
00350 
00351 nsresult
00352 nsWebScriptsAccess::CreateEntry(nsIDOMNodeList* aAllowList,
00353                                 AccessInfoEntry** aEntry)
00354 {
00355   NS_ENSURE_ARG_POINTER(aAllowList);
00356   NS_ENSURE_ARG_POINTER(aEntry);
00357   *aEntry = nsnull;
00358 
00359   nsAutoPtr<AccessInfoEntry> entry(new AccessInfoEntry());
00360   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
00361 
00362   PRUint32 count;
00363   aAllowList->GetLength(&count);
00364 
00365   PRUint32 index; 
00366   nsCOMPtr<nsIDOMNode> node;
00367   nsAutoString type, from;
00368   for (index = 0; index < count; index++) {
00369     aAllowList->Item(index, getter_AddRefs(node));
00370     NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
00371      
00372     nsCOMPtr<nsIDOMElement> element(do_QueryInterface(node));
00373     element->GetAttribute(kTypeAttr, type);
00374     element->GetAttribute(kFromAttr, from);
00375 
00376     PRBool found_type = !type.IsEmpty();
00377     PRBool found_from = !from.IsEmpty();
00378 
00379     if (!found_type && !found_from) {
00380       // Minor optimization - If the "type" and "from"
00381       // attributes aren't present then no need to check
00382       // for attributes in other "allow" elements because
00383       // access will be granted to all regardless.
00384       entry->mFlags |= WSA_GRANT_ACCESS_TO_ALL;
00385       break;
00386     }
00387 
00388     nsAutoPtr<AccessInfo> access_info(new AccessInfo());
00389     NS_ENSURE_TRUE(access_info, NS_ERROR_OUT_OF_MEMORY);
00390     
00391     if (found_type) {
00392       access_info->mType = ToNewUnicode(type);
00393       NS_ENSURE_TRUE(access_info->mType, NS_ERROR_OUT_OF_MEMORY);
00394     }
00395 
00396     if (found_from) {
00397       access_info->mFrom = ToNewUnicode(from);
00398       NS_ENSURE_TRUE(access_info->mFrom, NS_ERROR_OUT_OF_MEMORY);
00399     }
00400 
00401     entry->mInfoArray.AppendElement(access_info.forget());
00402     
00403     type.Truncate();
00404     from.Truncate();
00405   }
00406 
00407   *aEntry = entry.forget();
00408 
00409   return NS_OK;
00410 }
00411 
00412 nsresult
00413 nsWebScriptsAccess::CreateDelegatedEntry(AccessInfoEntry** aEntry)
00414 {
00415   NS_ENSURE_ARG_POINTER(aEntry);
00416   *aEntry = nsnull;
00417   
00418   nsresult rv;
00419   nsCOMPtr<nsIURL> url(do_QueryInterface(mServiceURI, &rv));
00420   NS_ENSURE_SUCCESS(rv, rv);
00421   
00422   nsCAutoString path;
00423   url->GetPrePath(path);
00424   nsCAutoString directory;
00425   url->GetDirectory(directory);
00426   path += directory;
00427 
00428   return CreateEntry(path.get(), PR_TRUE, aEntry);
00429 }
00430 
00431 nsresult
00432 nsWebScriptsAccess::CheckAccess(AccessInfoEntry* aEntry,
00433                                 const nsAString& aRequestType, 
00434                                 PRBool* aAccessGranted)
00435 {
00436 #ifdef DEBUG
00437   static PRBool verified = PR_FALSE;
00438   if (!verified) {
00439     verified = PR_TRUE;
00440     nsWSAUtils::VerifyIsEqual();
00441   }
00442 #endif
00443 
00444   *aAccessGranted = PR_FALSE;
00445   NS_ENSURE_ARG_POINTER(aEntry);
00446 
00447   nsresult rv = NS_OK;
00448   if (aEntry->mFlags & WSA_FILE_NOT_FOUND) {
00449     if (aEntry->mFlags & HAS_MASTER_SERVICE_DECISION) {
00450       if (aEntry->mFlags & SERVICE_LISTED_PUBLIC)
00451          *aAccessGranted = PR_TRUE;
00452       return rv;
00453     }
00454     nsCAutoString fqdn;
00455     rv = nsWSAUtils::GetOfficialHostName(mServiceURI, fqdn);
00456     if (NS_FAILED(rv) || fqdn.IsEmpty())
00457       return rv;
00458     
00459     PRBool isPublic = PR_FALSE;
00460     rv = IsPublicService(fqdn.get(), &isPublic);
00461     if (NS_SUCCEEDED(rv)) {
00462       if (isPublic) {
00463         aEntry->mFlags |= SERVICE_LISTED_PUBLIC;
00464         *aAccessGranted = PR_TRUE;
00465       }
00466       aEntry->mFlags |= HAS_MASTER_SERVICE_DECISION; 
00467     }
00468     return rv;
00469   }
00470  
00471   if (aEntry->mFlags & WSA_GRANT_ACCESS_TO_ALL) {
00472     *aAccessGranted = PR_TRUE;
00473     return NS_OK;
00474   }
00475 
00476   nsCOMPtr<nsIURI> codebase_uri;
00477   rv = GetCodebaseURI(getter_AddRefs(codebase_uri));
00478   NS_ENSURE_SUCCESS(rv, rv);
00479 
00480   nsXPIDLCString tmp;
00481   codebase_uri->GetSpec(tmp);
00482   const nsAString& codebase = NS_ConvertUTF8toUCS2(tmp);
00483 
00484   PRUint32 count = aEntry->mInfoArray.Count();
00485   PRUint32 index;
00486   for (index = 0; index < count; index++) {
00487     AccessInfo* access_info = 
00488       NS_REINTERPRET_CAST(AccessInfo*, aEntry->mInfoArray.ElementAt(index));
00489     NS_ASSERTION(access_info, "Entry is missing attribute information");
00490     
00491     if (!access_info->mType || kAny.Equals(access_info->mType) || 
00492         aRequestType.Equals(access_info->mType)) {
00493       if (!access_info->mFrom) {
00494         // If "from" is not specified, then all scripts will be  allowed 
00495         *aAccessGranted = PR_TRUE;
00496         break;
00497       }
00498       else {
00499         if (nsWSAUtils::IsEqual(nsDependentString(access_info->mFrom), 
00500                                 codebase)) {
00501           *aAccessGranted = PR_TRUE;
00502           break;
00503         }
00504       }
00505     }
00506   }
00507 
00508   return NS_OK;
00509 }
00510 
00520 nsresult
00521 nsWebScriptsAccess::ValidateDocument(nsIDOMDocument* aDocument,
00522                                      PRBool* aIsValid)
00523 {
00524   NS_ENSURE_ARG_POINTER(aDocument);
00525 
00526   *aIsValid = PR_FALSE;
00527   nsCOMPtr<nsIDOMElement> rootElement;
00528   aDocument->GetDocumentElement(getter_AddRefs(rootElement));
00529   
00530   nsAutoString ns;
00531   nsAutoString name;
00532   nsresult rv = rootElement->GetNamespaceURI(ns);
00533   if (NS_FAILED(rv))
00534     return rv;
00535   rootElement->GetLocalName(name);
00536   if (NS_FAILED(rv))
00537     return rv;
00538   
00539   if (!ns.Equals(kNamespace2002)) {
00540     const PRUnichar *inputs[1]  = { ns.get() };
00541     return nsWSAUtils::ReportError(
00542                          NS_LITERAL_STRING("UnsupportedNamespace").get(), 
00543                          inputs, 1);
00544   }
00545   if (!name.Equals(kWebScriptAccessTag)) {
00546     const PRUnichar *inputs[1]  = { name.get() };
00547     return nsWSAUtils::ReportError(
00548                          NS_LITERAL_STRING("UnknownRootElement").get(), 
00549                          inputs, 1);
00550   }
00551 
00552   nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
00553   NS_ENSURE_TRUE(rootNode, NS_ERROR_UNEXPECTED);
00554 
00555   nsCOMPtr<nsIDOMNodeList> children;
00556   rootNode->GetChildNodes(getter_AddRefs(children));
00557   NS_ENSURE_TRUE(children, NS_ERROR_UNEXPECTED);
00558 
00559   PRUint32 length;
00560   children->GetLength(&length);
00561 
00562   PRBool hadDelegate = PR_FALSE;
00563   nsCOMPtr<nsIDOMNode> child, attr;
00564   nsCOMPtr<nsIDOMNamedNodeMap> attrs;
00565   PRUint32 i;
00566   for (i = 0; i < length; i++) {
00567     children->Item(i, getter_AddRefs(child));
00568     NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
00569   
00570     PRUint16 type;
00571     child->GetNodeType(&type);
00572 
00573     if (nsIDOMNode::ELEMENT_NODE == type) {
00574       rv = child->GetNamespaceURI(ns);
00575       if (NS_FAILED(rv))
00576         return rv;
00577       rv = child->GetLocalName(name);
00578       if (NS_FAILED(rv))
00579         return rv;
00580  
00581       if (!ns.Equals(kNamespace2002))
00582         continue; // ignore elements with different ns.
00583 
00584       PRBool hasChildNodes = PR_FALSE;
00585       if (name.Equals(kDelegateTag)) {
00586         // There can me no more than one delegate element.
00587         if (hadDelegate) {
00588           const PRUnichar *inputs[1] = { name.get() };
00589           return nsWSAUtils::ReportError(
00590                                NS_LITERAL_STRING("TooManyElements").get(), 
00591                                inputs, 1);
00592         }
00593         // Make sure that the delegate element is EMPTY.
00594         child->HasChildNodes(&hasChildNodes);
00595         if (hasChildNodes) {
00596           const PRUnichar *inputs[1] = { name.get() };
00597           return nsWSAUtils::ReportError(
00598                                NS_LITERAL_STRING("ElementNotEmpty").get(), 
00599                                inputs, 1);
00600         }
00601         hadDelegate = PR_TRUE;
00602       }
00603       else if (name.Equals(kAllowTag)) {
00604         // Make sure that the allow element is EMPTY.
00605         child->HasChildNodes(&hasChildNodes);
00606         if (hasChildNodes) {
00607           const PRUnichar *inputs[1] = { name.get() };
00608           return nsWSAUtils::ReportError(
00609                                NS_LITERAL_STRING("ElementNotEmpty").get(), 
00610                                inputs, 1);
00611         }
00612         rv = child->GetAttributes(getter_AddRefs(attrs));
00613         if (NS_FAILED(rv))
00614           return rv;
00615         
00616         PRUint32 count, i;
00617         attrs->GetLength(&count);
00618         for (i = 0; i < count; i++) {
00619           attrs->Item(i, getter_AddRefs(attr));
00620           if (attr) {
00621             rv = attr->GetLocalName(name);
00622             if (NS_FAILED(rv))
00623               return rv;
00624             if (!name.Equals(kTypeAttr) && !name.Equals(kFromAttr)) {
00625               const PRUnichar *inputs[1] = { name.get() };
00626               return nsWSAUtils::ReportError(
00627                                    NS_LITERAL_STRING("UnknownAttribute").get(), 
00628                                    inputs, 1);
00629             }
00630           }
00631         }
00632       }
00633       else {
00634         const PRUnichar *inputs[1] = { name.get() };
00635         return nsWSAUtils::ReportError(
00636                              NS_LITERAL_STRING("UnknownElement").get(), 
00637                              inputs, 1);
00638       }
00639 
00640     }
00641   }
00642   *aIsValid = PR_TRUE;
00643   return NS_OK;
00644 }
00645 
00646 static PRBool
00647 IsCharInSet(const char* aSet,
00648             const PRUnichar aChar)
00649 {
00650   PRUnichar ch;
00651   while ((ch = *aSet)) {
00652     if (aChar == PRUnichar(ch)) {
00653       return PR_TRUE;
00654     }
00655     ++aSet;
00656   }
00657   return PR_FALSE;
00658 }
00659 
00660 nsresult
00661 nsWebScriptsAccess::IsPublicService(const char* aHost, PRBool* aReturn)
00662 {
00663   *aReturn = PR_FALSE;
00664   nsresult rv = NS_OK;
00665   // Cache the master services included in the prefs.
00666   if (mMasterServices.Count() == 0) {
00667     nsCOMPtr<nsIPrefBranch> prefBranch = 
00668       do_GetService(NS_PREFSERVICE_CONTRACTID);
00669   
00670     if (!prefBranch)
00671       return rv;
00672 
00673     nsXPIDLCString value;
00674     rv = prefBranch->GetCharPref("xml.webservice.security.masterservices",
00675                                  getter_Copies(value));
00676     
00677     if (NS_FAILED(rv) || value.IsEmpty())
00678       return NS_OK;
00679       
00680     nsACString::const_iterator begin, end, curr;
00681     nsACString::const_iterator uri_begin, uri_end;
00682     value.BeginReading(begin);
00683     value.EndReading(end);
00684     
00685     // Parse the comma separated pref. value
00686     static const char* kWhitespace = " \n\r\t\b";
00687     while (begin != end) {
00688       curr = begin;
00689       // strip leading whitespaces
00690       while (IsCharInSet(kWhitespace, *curr) && ++curr != end);
00691       uri_begin = curr;
00692       // consume until the delimiter ( comma ).
00693       while (curr != end && *curr != ',')
00694         ++curr;
00695       uri_end = curr;
00696       // strip trailing whitespaces
00697       while (uri_end != uri_begin) {
00698         if (!IsCharInSet(kWhitespace, *(--uri_end))) {
00699           ++uri_end; // include the last non whitespace char.
00700           break;
00701         }
00702       }
00703       const nsAFlatString& transportURI =
00704         NS_ConvertUTF8toUCS2(Substring(uri_begin, uri_end));
00705       if (!transportURI.IsEmpty())
00706         mMasterServices.AppendString(transportURI);
00707       begin = (*curr == ',' && curr != end) ? ++curr : curr;
00708     }
00709   }
00710 
00711   // Do nothing if the pref value turns out to be 
00712   // strings with nothing but whitespaces.
00713   if (mMasterServices.Count() == 0)
00714     return rv;
00715 
00716   // Allocate param block.
00717   nsISOAPParameter** bodyBlocks = 
00718     NS_STATIC_CAST(nsISOAPParameter**,
00719                   nsMemory::Alloc(1 * sizeof(nsISOAPParameter*)));
00720   if (!bodyBlocks)
00721     return NS_ERROR_OUT_OF_MEMORY;
00722 
00723   rv = 
00724     CallCreateInstance(NS_SOAPPARAMETER_CONTRACTID, &bodyBlocks[0]);
00725   
00726   if (NS_FAILED(rv)) {
00727     NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(0, bodyBlocks);
00728     return rv;
00729   }
00730 
00731   nsCOMPtr<nsISOAPBlock> block = do_QueryInterface(bodyBlocks[0], &rv);
00732 
00733   if (NS_FAILED(rv))
00734     return rv;
00735 
00736   block->SetName(NS_LITERAL_STRING("fqdn"));
00737 
00738   nsCOMPtr<nsIWritableVariant> variant =
00739     do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
00740 
00741   if (NS_FAILED(rv))
00742     return rv;
00743 
00744   variant->SetAsString(aHost);
00745 
00746   block->SetValue(variant);
00747 
00748     // Create the call instance
00749   nsCOMPtr<nsISOAPCall> call = 
00750     do_CreateInstance(NS_SOAPCALL_CONTRACTID, &rv);
00751   
00752   if (NS_FAILED(rv))
00753     return rv;
00754 
00755   nsCOMPtr<nsISOAPEncoding> encoding =
00756     do_CreateInstance(NS_SOAPENCODING_CONTRACTID, &rv);
00757   
00758   if (NS_FAILED(rv))
00759     return rv;
00760 
00761   call->SetEncoding(encoding);
00762   
00763   // Since the SOAP request to the central server will be made
00764   // from the native code we can safely override cross-domain 
00765   // checks by pushing in a null jscontext on the context stack.
00766   nsCOMPtr<nsIJSContextStack> stack = 
00767     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
00768   if (stack)
00769     stack->Push(nsnull);
00770 
00771   nsCOMPtr<nsISOAPResponse> response;
00772   PRInt32 i, count = mMasterServices.Count();
00773   for (i = 0; i < count && !response; i++) {
00774     rv = 
00775       call->SetTransportURI(*mMasterServices.StringAt(i));
00776     
00777     if (NS_FAILED(rv))
00778       break;
00779     
00780     rv = call->Encode(nsISOAPMessage::VERSION_1_1,
00781                       kIsServicePublic, 
00782                       kNamespace2002, // The target URI
00783                       0, 0, 1, bodyBlocks);
00784     if (NS_FAILED(rv))
00785       break;
00786   
00787     call->Invoke(getter_AddRefs(response)); // XXX - How to handle time out and 404?
00788   }
00789   
00790   if (stack) {
00791     JSContext* cx;
00792     stack->Pop(&cx);
00793     NS_ASSERTION(!cx, "context should be null");
00794   }
00795 
00796   if (!response)
00797     return rv;
00798   
00799   nsCOMPtr<nsISOAPFault> fault;
00800   response->GetFault(getter_AddRefs(fault));
00801     
00802   if (fault) {
00803     nsAutoString faultNamespaceURI, faultCode, faultString;
00804     fault->GetFaultNamespaceURI(faultNamespaceURI);
00805     fault->GetFaultCode(faultCode);
00806     fault->GetFaultString(faultString);
00807     const PRUnichar *inputs[5]  = 
00808       { 
00809         kNamespace2002.get(),
00810         kIsServicePublic.get(),
00811         faultNamespaceURI.get(),
00812         faultCode.get(),
00813         faultString.get()
00814       };
00815     return nsWSAUtils::ReportError(
00816                         NS_LITERAL_STRING("SOAPFault").get(), 
00817                         inputs, 5);
00818   }
00819   else {
00820     PRUint32 bodyCount;
00821     rv = response->GetParameters(PR_FALSE, &bodyCount, &bodyBlocks);
00822     NS_ASSERTION(bodyBlocks, "insufficient information");
00823 
00824     if (!bodyBlocks || NS_FAILED(rv))
00825       return rv;
00826     
00827     NS_ASSERTION(bodyCount == 1, "body seems to contain unnecessary information.");
00828     block = do_QueryInterface(bodyBlocks[0], &rv);
00829     
00830     if (NS_FAILED(rv))
00831       return rv;
00832     
00833     nsCOMPtr<nsIVariant> value;
00834     rv = block->GetValue(getter_AddRefs(value));
00835     
00836     if (NS_FAILED(rv) || !value)
00837       return rv;
00838     
00839     rv = value->GetAsBool(aReturn);
00840   }
00841 
00842   return rv;
00843 }
00844