Back to index

lightning-sunbird  0.9+nobinonly
nsContentPolicy.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 // vim: ft=cpp tw=78 sw=4 et ts=8
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Zero-Knowledge Systems, Inc.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "prlog.h"
00040 
00041 #include "nsISupports.h"
00042 #include "nsXPCOM.h"
00043 #include "nsISupportsPrimitives.h"
00044 #include "nsXPIDLString.h"
00045 #include "nsContentPolicyUtils.h"
00046 #include "nsContentPolicy.h"
00047 #include "nsICategoryManager.h"
00048 #include "nsIURI.h"
00049 #include "nsIDOMNode.h"
00050 #include "nsIDOMWindow.h"
00051 #include "nsIContent.h"
00052 
00053 NS_IMPL_ISUPPORTS1(nsContentPolicy, nsIContentPolicy)
00054 
00055 #ifdef PR_LOGGING
00056 static PRLogModuleInfo* gConPolLog;
00057 #endif
00058 
00059 nsresult
00060 NS_NewContentPolicy(nsIContentPolicy **aResult)
00061 {
00062   *aResult = new nsContentPolicy;
00063   if (!*aResult)
00064       return NS_ERROR_OUT_OF_MEMORY;
00065   NS_ADDREF(*aResult);
00066   return NS_OK;
00067 }
00068 
00069 /*
00070  * This constructor does far too much.  I wish there was a way to get
00071  * an Init method called by the service manager after the factory
00072  * returned the new object, so that errors could be propagated back to
00073  * the caller correctly.
00074  */
00075 nsContentPolicy::nsContentPolicy()
00076 {
00077 #ifdef PR_LOGGING
00078     if (! gConPolLog) {
00079         gConPolLog = PR_NewLogModule("nsContentPolicy");
00080     }
00081 #endif
00082     nsresult rv;
00083     nsCOMPtr<nsICategoryManager> catman = 
00084              do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
00085     if (NS_FAILED(rv))
00086         return; /* log an error? */
00087 
00088     /*
00089      * I'd like to use GetCategoryContents, so that I can size the array
00090      * correctly on the first go and avoid the enumerator overhead, but it's
00091      * not yet implemented (see nsCategoryManager.cpp).  No biggie, I guess.
00092      */
00093     nsCOMPtr<nsISimpleEnumerator> catEnum;
00094     rv = catman->EnumerateCategory(NS_CONTENTPOLICY_CATEGORY,
00095                                    getter_AddRefs(catEnum));
00096     if (NS_FAILED(rv))
00097         return; /* no category, no problem */
00098 
00099     PRBool hasMore;
00100     if (NS_FAILED(catEnum->HasMoreElements(&hasMore)) || !hasMore)
00101        return;
00102     
00103     /* 
00104      * Populate mPolicies with policy services named by contractids in the
00105      * "content-policy" category.
00106      */
00107     nsCOMPtr<nsISupports> item;
00108     while (NS_SUCCEEDED(catEnum->GetNext(getter_AddRefs(item)))) {
00109         nsCOMPtr<nsISupportsCString> string = do_QueryInterface(item, &rv);
00110         if (NS_FAILED(rv))
00111             continue;
00112 
00113         nsCAutoString contractid;
00114         if (NS_FAILED(string->GetData(contractid)))
00115             continue;
00116 
00117         PR_LOG(gConPolLog, PR_LOG_DEBUG,
00118                 ("POLICY: loading %s\n", contractid.get()));
00119 
00120         /*
00121          * Create this policy service and add to mPolicies.
00122          *
00123          * Should we try to parse as a CID, in case the component prefers to be
00124          * registered that way?
00125          */
00126         nsCOMPtr<nsIContentPolicy> policy = do_GetService(contractid.get(),
00127                                                           &rv);
00128         if (NS_SUCCEEDED(rv) && policy) {
00129             mPolicies.AppendObject(policy);
00130         }
00131     }
00132        
00133 }
00134 
00135 nsContentPolicy::~nsContentPolicy()
00136 {
00137 }
00138 
00139 #ifdef DEBUG
00140 #define WARN_IF_URI_UNINITIALIZED(uri,name)                         \
00141   PR_BEGIN_MACRO                                                    \
00142     if ((uri)) {                                                    \
00143         nsCAutoString spec;                                         \
00144         (uri)->GetAsciiSpec(spec);                                  \
00145         if (spec.IsEmpty()) {                                       \
00146             NS_WARNING(name " is uninitialized, fix caller");       \
00147         }                                                           \
00148     }                                                               \
00149   PR_END_MACRO
00150 
00151 #else  // ! defined(DEBUG)
00152 
00153 #define WARN_IF_URI_UNINITIALIZED(uri,name)
00154 
00155 #endif // defined(DEBUG)
00156 
00157 inline nsresult
00158 nsContentPolicy::CheckPolicy(CPMethod          policyMethod,
00159                              PRUint32          contentType,
00160                              nsIURI           *contentLocation,
00161                              nsIURI           *requestingLocation,
00162                              nsISupports      *requestingContext,
00163                              const nsACString &mimeType,
00164                              nsISupports      *extra,
00165                              PRInt16           *decision)
00166 {
00167     //sanity-check passed-through parameters
00168     NS_PRECONDITION(decision, "Null out pointer");
00169     WARN_IF_URI_UNINITIALIZED(contentLocation, "Request URI");
00170     WARN_IF_URI_UNINITIALIZED(requestingLocation, "Requesting URI");
00171 
00172 #ifdef DEBUG
00173     {
00174         nsCOMPtr<nsIDOMNode> node(do_QueryInterface(requestingContext));
00175         nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(requestingContext));
00176         NS_ASSERTION(!requestingContext || node || window,
00177                      "Context should be a DOM node or a DOM window!");
00178     }
00179 #endif
00180 
00181     /*
00182      * There might not be a requestinglocation. This can happen for
00183      * iframes with an image as src. Get the uri from the dom node.
00184      * See bug 254510
00185      */
00186     if (!requestingLocation) {
00187         nsCOMPtr<nsIDocument> doc;
00188         nsCOMPtr<nsIContent> node = do_QueryInterface(requestingContext);
00189         if (node) {
00190             doc = node->GetOwnerDoc();
00191         }
00192         if (!doc) {
00193             doc = do_QueryInterface(requestingContext);
00194         }
00195         if (doc) {
00196             requestingLocation = doc->GetDocumentURI();
00197         }
00198     }
00199 
00200     PRInt32 count = mPolicies.Count();
00201     nsresult rv = NS_OK;
00202 
00203     /* 
00204      * Enumerate mPolicies and ask each of them, taking the logical AND of
00205      * their permissions.
00206      */
00207     for (PRInt32 i = 0; i < count; i++) {
00208         nsIContentPolicy *policy = mPolicies[i];
00209         if (!policy) { //shouldn't happen
00210             NS_ERROR("Somehow a null policy got into the list");
00211             continue;
00212         }
00213 
00214         /* check the appropriate policy */
00215         rv = (policy->*policyMethod)(contentType, contentLocation,
00216                                      requestingLocation, requestingContext,
00217                                      mimeType, extra, decision);
00218 
00219         if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) {
00220             /* policy says no, no point continuing to check */
00221             return NS_OK;
00222         }
00223     }
00224 
00225     // everyone returned failure, or no policies: sanitize result
00226     *decision = nsIContentPolicy::ACCEPT;
00227     return NS_OK;
00228 }
00229 
00230 #ifdef PR_LOGGING
00231 
00232 //uses the parameters from ShouldXYZ to produce and log a message
00233 //logType must be a literal string constant
00234 #define LOG_CHECK(logType)                                                    \
00235   PR_BEGIN_MACRO                                                              \
00236     /* skip all this nonsense if the call failed */                           \
00237     if (NS_SUCCEEDED(rv)) {                                                   \
00238       const char *resultName;                                                 \
00239       if (decision) {                                                         \
00240         resultName = NS_CP_ResponseName(*decision);                           \
00241       } else {                                                                \
00242         resultName = "(null ptr)";                                            \
00243       }                                                                       \
00244       nsCAutoString spec("None");                                             \
00245       if (contentLocation) {                                                  \
00246           contentLocation->GetSpec(spec);                                     \
00247       }                                                                       \
00248       nsCAutoString refSpec("None");                                          \
00249       if (requestingLocation) {                                               \
00250           requestingLocation->GetSpec(refSpec);                               \
00251       }                                                                       \
00252       PR_LOG(gConPolLog, PR_LOG_DEBUG,                                        \
00253              ("Content Policy: " logType ": <%s> <Ref:%s> result=%s",         \
00254               spec.get(), refSpec.get(), resultName)                          \
00255              );                                                               \
00256     }                                                                         \
00257   PR_END_MACRO
00258 
00259 #else //!defined(PR_LOGGING)
00260 
00261 #define LOG_CHECK(logType)
00262 
00263 #endif //!defined(PR_LOGGING)
00264 
00265 NS_IMETHODIMP
00266 nsContentPolicy::ShouldLoad(PRUint32          contentType,
00267                             nsIURI           *contentLocation,
00268                             nsIURI           *requestingLocation,
00269                             nsISupports      *requestingContext,
00270                             const nsACString &mimeType,
00271                             nsISupports      *extra,
00272                             PRInt16          *decision)
00273 {
00274     // ShouldProcess does not need a content location, but we do
00275     NS_PRECONDITION(contentLocation, "Must provide request location");
00276     nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldLoad, contentType,
00277                               contentLocation, requestingLocation,
00278                               requestingContext, mimeType, extra, decision);
00279     LOG_CHECK("ShouldLoad");
00280 
00281     return rv;
00282 }
00283 
00284 NS_IMETHODIMP
00285 nsContentPolicy::ShouldProcess(PRUint32          contentType,
00286                                nsIURI           *contentLocation,
00287                                nsIURI           *requestingLocation,
00288                                nsISupports      *requestingContext,
00289                                const nsACString &mimeType,
00290                                nsISupports      *extra,
00291                                PRInt16          *decision)
00292 {
00293     nsresult rv = CheckPolicy(&nsIContentPolicy::ShouldProcess, contentType,
00294                               contentLocation, requestingLocation,
00295                               requestingContext, mimeType, extra, decision);
00296     LOG_CHECK("ShouldProcess");
00297 
00298     return rv;
00299 }