Merge pull request #2530 from lambdageek/monoerror-mono_string_new
[mono.git] / mcs / class / System.Web / System.Web / SiteMapProvider.cs
1 //
2 // System.Web.SiteMapProvider
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      Lluis Sanchez Gual (lluis@novell.com)
7 //
8 // (C) 2003 Ben Maurer
9 // (C) 2005-2009 Novell, Inc (http://www.novell.com)
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.ComponentModel;
34 using System.Collections;
35 using System.Collections.Specialized;
36 using System.Text;
37 using System.Configuration.Provider;
38 using System.Web.Util;
39 using System.Globalization;
40 using System.Web.Configuration;
41
42 namespace System.Web
43 {
44         public abstract class SiteMapProvider : ProviderBase
45         {
46                 static readonly object siteMapResolveEvent = new object ();
47                 
48                 internal object this_lock = new object ();
49                 
50                 bool enableLocalization;
51                 SiteMapProvider parentProvider;
52                 SiteMapProvider rootProviderCache;
53                 bool securityTrimming;
54                 object resolveLock = new Object();
55                 bool resolving;
56
57                 EventHandlerList events = new EventHandlerList ();
58                 
59                 public event SiteMapResolveEventHandler SiteMapResolve {
60                         add { events.AddHandler (siteMapResolveEvent, value); }
61                         remove { events.RemoveHandler (siteMapResolveEvent, value); }
62                 }
63                 
64                 protected virtual void AddNode (SiteMapNode node)
65                 {
66                         AddNode (node, null);
67                 }
68                 
69                 internal protected virtual void AddNode (SiteMapNode node, SiteMapNode parentNode)
70                 {
71                         throw new NotImplementedException ();
72                 }
73
74                 public virtual SiteMapNode FindSiteMapNode (HttpContext context)
75                 {
76                         if (context == null)
77                                 return null;
78
79                         HttpRequest req = context.Request;
80                         if (req == null)
81                                 return null;
82                         
83                         SiteMapNode ret = this.FindSiteMapNode (req.RawUrl);
84                         if (ret == null)
85                                 ret = this.FindSiteMapNode (req.Path);
86                         return ret;
87                 }
88
89                 public abstract SiteMapNode FindSiteMapNode (string rawUrl);
90                 
91                 public virtual SiteMapNode FindSiteMapNodeFromKey (string key)
92                 {
93                         /* msdn2 says this function always returns
94                          * null, but it seems to just call
95                          * FindSiteMapNode(string rawUrl) */
96                         return FindSiteMapNode (key);
97                 }
98
99                 public abstract SiteMapNodeCollection GetChildNodes (SiteMapNode node);
100                 
101                 public virtual SiteMapNode GetCurrentNodeAndHintAncestorNodes (int upLevel)
102                 {
103                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
104
105                         return CurrentNode;
106                 }
107                 
108                 public virtual SiteMapNode GetCurrentNodeAndHintNeighborhoodNodes (int upLevel, int downLevel)
109                 {
110                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
111                         if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
112                         
113                         return CurrentNode;
114                 }
115
116                 public abstract SiteMapNode GetParentNode (SiteMapNode node);
117                 
118                 public virtual SiteMapNode GetParentNodeRelativeToCurrentNodeAndHintDownFromParent (int walkupLevels, int relativeDepthFromWalkup)
119                 {
120                         if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
121                         if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
122                         
123                         SiteMapNode node = GetCurrentNodeAndHintAncestorNodes (walkupLevels);
124                         for (int n=0; n<walkupLevels && node != null; n++)
125                                 node = GetParentNode (node);
126                                 
127                         if (node == null) return null;
128
129                         HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
130                         return node;
131                 }
132                 
133                 public virtual SiteMapNode GetParentNodeRelativeToNodeAndHintDownFromParent (SiteMapNode node, int walkupLevels, int relativeDepthFromWalkup)
134                 {
135                         if (walkupLevels < 0) throw new ArgumentOutOfRangeException ("walkupLevels");
136                         if (relativeDepthFromWalkup < 0) throw new ArgumentOutOfRangeException ("relativeDepthFromWalkup");
137                         if (node == null) throw new ArgumentNullException ("node");
138                         
139                         HintAncestorNodes (node, walkupLevels);
140                         for (int n=0; n<walkupLevels && node != null; n++)
141                                 node = GetParentNode (node);
142                                 
143                         if (node == null) return null;
144                         
145                         HintNeighborhoodNodes (node, 0, relativeDepthFromWalkup);
146                         return node;
147                 }
148                 
149                 protected internal abstract SiteMapNode GetRootNodeCore ();
150                 
151                 protected static SiteMapNode GetRootNodeCoreFromProvider (SiteMapProvider provider)
152                 {
153                         return provider.GetRootNodeCore ();
154                 }
155                 
156                 public virtual void HintAncestorNodes (SiteMapNode node, int upLevel)
157                 {
158                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
159                         if (node == null) throw new ArgumentNullException ("node");
160                 }
161                 
162                 public virtual void HintNeighborhoodNodes (SiteMapNode node, int upLevel, int downLevel)
163                 {
164                         if (upLevel < -1) throw new ArgumentOutOfRangeException ("upLevel");
165                         if (downLevel < -1) throw new ArgumentOutOfRangeException ("downLevel");
166                         if (node == null) throw new ArgumentNullException ("node");
167                 }
168                 
169                 protected virtual void RemoveNode (SiteMapNode node)
170                 {
171                         throw new NotImplementedException ();
172                 }
173
174                 public override void Initialize (string name, NameValueCollection attributes)
175                 {
176                         base.Initialize (name, attributes);
177                         if (attributes ["securityTrimmingEnabled"] != null)
178                                 securityTrimming = (bool) Convert.ChangeType (attributes ["securityTrimmingEnabled"], typeof (bool));
179                 }
180                 
181                 [MonoTODO ("need to implement cases 2 and 3")]
182                 public virtual bool IsAccessibleToUser (HttpContext context, SiteMapNode node)
183                 {
184                         if (context == null) throw new ArgumentNullException ("context");
185                         if (node == null) throw new ArgumentNullException ("node");
186
187                         if (!SecurityTrimmingEnabled)
188                                 return true;
189
190                         /* The node is accessible (according to msdn2) if:
191                          *
192                          * 1. The Roles exists on node and the current user is in at least one of the specified roles.
193                          *
194                          * 2. The current thread has an associated WindowsIdentity that has file access to the requested URL and
195                          * the URL is located within the directory structure for the application.
196                          *
197                          * 3. The current user is authorized specifically for the requested URL in the authorization element for
198                          * the current application and the URL is located within the directory structure for the application. 
199                         */
200
201                         /* 1. */
202                         IList roles = node.Roles;
203                         if (roles != null && roles.Count > 0) {
204                                 foreach (string rolename in roles)
205                                         if (rolename == "*" || context.User.IsInRole (rolename))
206                                                 return true;
207                         }
208                         
209                         /* 2. */
210                         /* XXX */
211
212                         /* 3. */
213                         string url = node.Url;
214                         if(!String.IsNullOrEmpty(url)) {
215                                 // TODO check url is located within the current application
216
217                                 if (VirtualPathUtility.IsAppRelative (url) || !VirtualPathUtility.IsAbsolute (url))
218                                         url = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (HttpRuntime.AppDomainAppVirtualPath), url);
219
220                                 AuthorizationSection config = (AuthorizationSection) WebConfigurationManager.GetSection (
221                                         "system.web/authorization",
222                                         url);
223                                 if (config != null)
224                                         return config.IsValidUser (context.User, context.Request.HttpMethod);
225                         }
226
227                         return false;
228                 }
229                 
230                 public virtual SiteMapNode CurrentNode {
231                         get {
232                                 if (HttpContext.Current != null) {
233                                         SiteMapNode ret = ResolveSiteMapNode (HttpContext.Current);
234                                         if (ret != null) return ret;
235                                         return FindSiteMapNode (HttpContext.Current);
236                                 } else
237                                         return null;
238                         }
239                 }
240                 
241                 public virtual SiteMapProvider ParentProvider {
242                         get { return parentProvider; }
243                         set { parentProvider = value; }
244                 }
245                 
246                 public virtual SiteMapProvider RootProvider {
247                         get {
248                                 lock (this_lock) {
249                                         if (rootProviderCache == null) {
250                                                 SiteMapProvider current = this;
251                                                 while (current.ParentProvider != null)
252                                                         current = current.ParentProvider;
253                                                 
254                                                 rootProviderCache = current;
255                                         }
256                                 }
257                                 return rootProviderCache;
258                         }
259                 }
260                 
261                 protected SiteMapNode ResolveSiteMapNode (HttpContext context)
262                 {
263                         SiteMapResolveEventHandler eh = events [siteMapResolveEvent] as SiteMapResolveEventHandler;
264
265                         if (eh != null) {
266                                 lock (resolveLock) {
267                                         if (resolving)
268                                                 return null;
269                                         resolving = true;
270                                         SiteMapResolveEventArgs args = new SiteMapResolveEventArgs (context, this);
271                                         SiteMapNode r = eh (this, args);
272                                         resolving = false;
273                                         return r;
274                                 }
275                         } else
276                                 return null;
277                 }
278                 
279                 public bool EnableLocalization {
280                         get { return enableLocalization; }
281                         set { enableLocalization = value; }
282                 }
283                 
284                 public bool SecurityTrimmingEnabled {
285                         get { return securityTrimming; }
286                 }
287
288                 string resourceKey;
289                 public string ResourceKey {
290                         get { return resourceKey; }
291                         set { resourceKey = value; }
292                 }
293
294                 public virtual SiteMapNode RootNode {
295                         get {
296                                 SiteMapNode node = GetRootNodeCore ();
297                                 return ReturnNodeIfAccessible (node);
298                         }
299                 }
300
301                 internal static SiteMapNode ReturnNodeIfAccessible (SiteMapNode node)
302                 {
303                         if (node.IsAccessibleToUser (HttpContext.Current))
304                                 return node;
305                         else
306                                 throw new InvalidOperationException (); /* need
307                                                                          * a
308                                                                          * message
309                                                                          * here */
310                 }
311         }
312 }
313
314