Merge pull request #3962 from mkorkalo/fix_MonoBtlsContext_memory_leak
[mono.git] / mcs / class / referencesource / System.Web / SiteMapProvider.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SiteMapProvider.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /*
8  * Copyright (c) 2002 Microsoft Corporation
9  */
10 namespace System.Web {
11
12     using System;
13     using System.Collections;
14     using System.Collections.Specialized;
15     using System.Configuration.Provider;
16     using System.Web.Security;
17     using System.Web.UI;
18     using System.Web.Util;
19     using System.Security.Permissions;
20
21     public abstract class SiteMapProvider : ProviderBase {
22
23         private bool _securityTrimmingEnabled;
24         private bool _enableLocalization;
25         private String _resourceKey;
26
27         internal const String _securityTrimmingEnabledAttrName = "securityTrimmingEnabled";
28         private const string _allRoles = "*";
29
30         private SiteMapProvider _rootProvider;
31         private SiteMapProvider _parentProvider;
32         private object _resolutionTicket = new object();
33
34         internal readonly object _lock = new Object();
35
36         public virtual SiteMapNode CurrentNode {
37             get {
38                 HttpContext context = HttpContext.Current;
39                 SiteMapNode result = null;
40
41                 // First check the SiteMap resolve events.
42                 result = ResolveSiteMapNode(context);
43
44                 if (result == null) {
45                     result = FindSiteMapNode(context);
46                 }
47
48                 return ReturnNodeIfAccessible(result);
49             }
50         }
51
52         public bool EnableLocalization {
53             get {
54                 return _enableLocalization;
55             }
56             set {
57                 _enableLocalization = value;
58             }
59         }
60
61         // Parent provider
62         public virtual SiteMapProvider ParentProvider {
63             get {
64                 return _parentProvider;
65             }
66             set {
67                 _parentProvider = value;
68             }
69         }
70
71         public string ResourceKey {
72             get {
73                 return _resourceKey;
74             }
75             set {
76                 _resourceKey = value;
77             }
78         }
79
80         public virtual SiteMapProvider RootProvider {
81             get {
82                 if (_rootProvider == null) {
83                     lock (_lock) {
84                         if (_rootProvider == null) {
85                             Hashtable providers = new Hashtable();
86                             SiteMapProvider candidate = this;
87
88                             providers.Add(candidate, null);
89                             while (candidate.ParentProvider != null) {
90                                 if (providers.Contains(candidate.ParentProvider))
91                                     throw new ProviderException(SR.GetString(SR.SiteMapProvider_Circular_Provider));
92
93                                 candidate = candidate.ParentProvider;
94                                 providers.Add(candidate, null);
95                             }
96
97                             _rootProvider = candidate;
98                         }
99                     }
100                 }
101
102                 return _rootProvider;
103             }
104         }
105
106         public virtual SiteMapNode RootNode {
107             get {
108                 SiteMapNode node = GetRootNodeCore();
109                 return ReturnNodeIfAccessible(node);
110             }
111         }
112
113         public bool SecurityTrimmingEnabled {
114             get {
115                 return _securityTrimmingEnabled;
116             }
117         }
118
119         public event SiteMapResolveEventHandler SiteMapResolve;
120
121         /// <devdoc>
122         ///    <para>Add single node to provider.</para>
123         /// </devdoc>
124         protected virtual void AddNode(SiteMapNode node) {
125             AddNode(node, null);
126         }
127
128         protected internal virtual void AddNode(SiteMapNode node, SiteMapNode parentNode) {
129             throw new NotImplementedException();
130         }
131
132         public virtual SiteMapNode FindSiteMapNode(HttpContext context) {
133             if (context == null) {
134                 return null;
135             }
136
137             string rawUrl = context.Request.RawUrl;
138
139             SiteMapNode result = null;
140
141             // First check the RawUrl
142             result = FindSiteMapNode(rawUrl);
143
144             if (result == null) {
145                 int queryStringIndex = rawUrl.IndexOf("?", StringComparison.Ordinal);
146                 if (queryStringIndex != -1) {
147                     // check the RawUrl without querystring
148                     result = FindSiteMapNode(rawUrl.Substring(0, queryStringIndex));
149                 }
150
151                 if (result == null) {
152                     Page page = context.CurrentHandler as Page;
153                     if (page != null) {
154                         // Try without server side query strings
155                         string qs = page.ClientQueryString;
156                         if (qs.Length > 0) {
157                             result = FindSiteMapNode(context.Request.Path + "?" + qs);
158                         }
159                     }
160
161                     if (result == null) {
162                         // Check the request path
163                         result = FindSiteMapNode(context.Request.Path);
164                     }
165                 }
166             }
167
168             return result;
169         }
170
171         public virtual SiteMapNode FindSiteMapNodeFromKey(string key) {
172             return FindSiteMapNode(key);
173         }
174
175         public abstract SiteMapNode FindSiteMapNode(string rawUrl);
176
177         public abstract SiteMapNodeCollection GetChildNodes(SiteMapNode node);
178
179         public virtual SiteMapNode GetCurrentNodeAndHintAncestorNodes(int upLevel) {
180             if (upLevel < -1) {
181                 throw new ArgumentOutOfRangeException("upLevel");
182             }
183
184             return CurrentNode;
185         }
186
187         public virtual SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes(int upLevel, int downLevel) {
188             if (upLevel < -1) {
189                 throw new ArgumentOutOfRangeException("upLevel");
190             }
191
192             if (downLevel < -1) {
193                 throw new ArgumentOutOfRangeException("downLevel");
194             }
195
196             return CurrentNode;
197         }
198
199         public abstract SiteMapNode GetParentNode(SiteMapNode node);
200
201         public virtual SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent (
202             int walkupLevels, int relativeDepthFromWalkup) {
203
204             if (walkupLevels < 0) {
205                 throw new ArgumentOutOfRangeException("walkupLevels");
206             }
207
208             if (relativeDepthFromWalkup < 0) {
209                 throw new ArgumentOutOfRangeException("relativeDepthFromWalkup");
210             }
211
212             // First get current nodes and hints about its ancestors.
213             SiteMapNode currentNode = GetCurrentNodeAndHintAncestorNodes(walkupLevels);
214
215             // Simply return if the currentNode is null.
216             if (currentNode == null) {
217                 return null;
218             }
219
220             // Find the target node by walk up the parent tree.
221             SiteMapNode targetNode = GetParentNodesInternal(currentNode, walkupLevels);
222
223             if (targetNode == null) {
224                 return null;
225             }
226
227             // Get hints about its lower neighborhood nodes.
228             HintNeighborhoodNodes(targetNode, 0, relativeDepthFromWalkup);
229
230             return targetNode;
231         }
232
233         public virtual SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent(
234             SiteMapNode node, int walkupLevels, int relativeDepthFromWalkup) {
235
236             if (walkupLevels < 0) {
237                 throw new ArgumentOutOfRangeException("walkupLevels");
238             }
239
240             if (relativeDepthFromWalkup < 0) {
241                 throw new ArgumentOutOfRangeException("relativeDepthFromWalkup");
242             }
243
244             if (node == null) {
245                 throw new ArgumentNullException("node");
246             }
247
248             // First get hints about ancestor nodes;
249             HintAncestorNodes(node, walkupLevels);
250
251             // walk up the parent node until the target node is found.
252             SiteMapNode ancestorNode = GetParentNodesInternal(node, walkupLevels);
253
254             if (ancestorNode == null) {
255                 return null;
256             }
257
258             // Get hints about its neighthood nodes
259             HintNeighborhoodNodes(ancestorNode, 0, relativeDepthFromWalkup);
260
261             return ancestorNode;
262         }
263
264         private SiteMapNode GetParentNodesInternal(SiteMapNode node, int walkupLevels) {
265             Debug.Assert(node != null);
266             if (walkupLevels <= 0) {
267                 return node;
268             }
269
270             do {
271                 node = node.ParentNode;
272                 walkupLevels--;
273             } while (node != null && walkupLevels != 0);
274
275             return node;
276         }
277
278         /*
279          * A reference node that must be returned by all sitemap providers, this is
280          * required for the parent provider to keep track of the relations between
281          * two providers.
282          * For example, the parent provider uses this method to keep track of the parent
283          * node of child provider's root node.
284          */
285         protected internal abstract SiteMapNode GetRootNodeCore();
286
287         protected static SiteMapNode GetRootNodeCoreFromProvider(SiteMapProvider provider) {
288             return provider.GetRootNodeCore();
289         }
290
291         public virtual void HintAncestorNodes(SiteMapNode node, int upLevel) {
292             if (node == null) {
293                 throw new ArgumentNullException("node");
294             }
295
296             if (upLevel < -1) {
297                 throw new ArgumentOutOfRangeException("upLevel");
298             }
299         }
300
301         public virtual void HintNeighborhoodNodes(SiteMapNode node, int upLevel, int downLevel) {
302             if (node == null) {
303                 throw new ArgumentNullException("node");
304             }
305
306             if (upLevel < -1) {
307                 throw new ArgumentOutOfRangeException("upLevel");
308             }
309
310             if (downLevel < -1) {
311                 throw new ArgumentOutOfRangeException("downLevel");
312             }
313         }
314
315         public override void Initialize(string name, NameValueCollection attributes) {
316             if (attributes != null) {
317                 if (string.IsNullOrEmpty(attributes["description"])) {
318                     attributes.Remove("description");
319                     attributes.Add("description", this.GetType().Name);
320                 }
321
322                 ProviderUtil.GetAndRemoveBooleanAttribute(attributes, _securityTrimmingEnabledAttrName, Name, ref _securityTrimmingEnabled);
323             }
324
325             base.Initialize(name, attributes);
326         }
327
328         public virtual bool IsAccessibleToUser(HttpContext context, SiteMapNode node) {
329             if (node == null) {
330                 throw new ArgumentNullException("node");
331             }
332
333             if (context == null) {
334                 throw new ArgumentNullException("context");
335             }
336
337             if (!SecurityTrimmingEnabled) {
338                 return true;
339             }
340
341             if (node.Roles != null) {
342                 foreach (string role in node.Roles) {
343                     // Grant access if one of the roles is a "*".
344                     if (role == _allRoles || 
345                         context.User != null && context.User.IsInRole(role)) {
346                         return true;
347                     }
348                 }
349             }
350
351             VirtualPath virtualPath = node.VirtualPath;
352             if (virtualPath == null ||
353                 !virtualPath.IsWithinAppRoot) {
354
355                 return false;
356             }
357
358             return System.Web.UI.Util.IsUserAllowedToPath(context, virtualPath);
359         }
360
361         protected internal virtual void RemoveNode(SiteMapNode node) {
362             throw new NotImplementedException();
363         }
364
365         protected SiteMapNode ResolveSiteMapNode(HttpContext context) {
366             SiteMapResolveEventHandler eventHandler = SiteMapResolve;
367             if (eventHandler == null)
368                 return null;
369
370             if (!context.Items.Contains(_resolutionTicket)) {
371                 context.Items.Add(_resolutionTicket, true);
372
373                 try {
374                     Delegate[] ds = eventHandler.GetInvocationList();
375                     int len = ds.Length;
376                     for (int i = 0; i < len; i++) {
377                         SiteMapNode ret = ((SiteMapResolveEventHandler)ds[i])(this, new SiteMapResolveEventArgs(context, this));
378                         if (ret != null) {
379                             return ret;
380                         }
381                     }
382                 } finally {
383                     context.Items.Remove(_resolutionTicket);
384                 }
385             }
386
387
388             return null;
389         }
390
391         internal SiteMapNode ReturnNodeIfAccessible(SiteMapNode node) {
392             if (node != null && node.IsAccessibleToUser(HttpContext.Current)) {
393                 return node;
394             }
395
396             return null;
397         }
398     }
399 }