Merge pull request #757 from mlintner/master
[mono.git] / mcs / class / System.Web / System.Web.Configuration_2.0 / WebConfigurationHost.cs
1 //
2 // System.Web.Configuration.WebConfigurationHost.cs
3 //
4 // Authors:
5 //  Lluis Sanchez Gual (lluis@novell.com)
6 //  Marek Habersack <mhabersack@novell.com>
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 //
27 // Copyright (C) 2005-2009 Novell, Inc (http://www.novell.com)
28 //
29
30
31 using System;
32 using System.Collections;
33 using System.IO;
34 using System.Security;
35 using System.Configuration;
36 using System.Configuration.Internal;
37 using System.Web.Hosting;
38 using System.Web.Util;
39 using System.Reflection;
40
41 /*
42  * this class needs to be rewritten to support usage of the
43  * IRemoteWebConfigurationHostServer interface.  Once that's done, we
44  * need an implementation of that interface that talks (through a web
45  * service?) to a remote site..
46  *
47  * for now, though, just implement it as we do
48  * System.Configuration.InternalConfigurationHost, i.e. the local
49  * case.
50  */
51 namespace System.Web.Configuration
52 {
53         class WebConfigurationHost: IInternalConfigHost
54         {
55                 WebConfigurationFileMap map;
56                 const string MachinePath = ":machine:";
57                 const string MachineWebPath = ":web:";
58
59                 string appVirtualPath;
60                 
61                 public virtual object CreateConfigurationContext (string configPath, string locationSubPath)
62                 {
63                         return new WebContext (WebApplicationLevel.AtApplication /* XXX */,
64                                                "" /* site XXX */,
65                                                "" /* application path XXX */,
66                                                configPath,
67                                                locationSubPath);
68                 }
69                 
70                 public virtual object CreateDeprecatedConfigContext (string configPath)
71                 {
72                         return new HttpConfigurationContext(configPath);
73                 }
74                 
75                 public virtual string DecryptSection (string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
76                 {
77                         if (protectedSection == null)
78                                 throw new ArgumentNullException ("protectedSection");
79
80                         return protectedSection.EncryptSection (encryptedXml, protectionProvider);
81                 }
82                 
83                 public virtual void DeleteStream (string streamName)
84                 {
85                         File.Delete (streamName);
86                 }
87                 
88                 public virtual string EncryptSection (string clearXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
89                 {
90                         if (protectedSection == null)
91                                 throw new ArgumentNullException ("protectedSection");
92
93                         return protectedSection.EncryptSection (clearXml, protectionProvider);
94                 }
95                 
96                 public virtual string GetConfigPathFromLocationSubPath (string configPath, string locationSubPath)
97                 {
98                         if (!String.IsNullOrEmpty (locationSubPath) && !String.IsNullOrEmpty (configPath)) {
99                                 string relConfigPath = configPath.Length == 1 ? null : configPath.Substring (1) + "/";
100                                 if (relConfigPath != null && locationSubPath.StartsWith (relConfigPath, StringComparison.Ordinal))
101                                         locationSubPath = locationSubPath.Substring (relConfigPath.Length);
102                         }
103                         
104                         string ret = configPath + "/" + locationSubPath;
105                         if (!String.IsNullOrEmpty (ret) && ret [0] == '/')
106                                 return ret.Substring (1);
107                         
108                         return ret;
109                 }
110                 
111                 public virtual Type GetConfigType (string typeName, bool throwOnError)
112                 {
113                         Type type = HttpApplication.LoadType (typeName);
114                         if (type == null && throwOnError)
115                                 throw new ConfigurationErrorsException ("Type not found: '" + typeName + "'");
116                         return type;
117                 }
118                 
119                 public virtual string GetConfigTypeName (Type t)
120                 {
121                         return t.AssemblyQualifiedName;
122                 }
123                 
124                 public virtual void GetRestrictedPermissions (IInternalConfigRecord configRecord, out PermissionSet permissionSet,
125                                                               out bool isHostReady)
126                 {
127                         throw new NotImplementedException ();
128                 }
129                 
130                 public virtual string GetStreamName (string configPath)
131                 {
132                         if (configPath == MachinePath) {
133                                 if (map == null)
134                                         return System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
135                                 else
136                                         return map.MachineConfigFilename;
137                         } else if (configPath == MachineWebPath) {
138                                 string mdir;
139
140                                 if (map == null)
141                                         mdir = Path.GetDirectoryName (System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile);
142                                 else
143                                         mdir = Path.GetDirectoryName (map.MachineConfigFilename);
144
145                                 return GetWebConfigFileName (mdir);
146                         }
147                         
148                         string dir = MapPath (configPath);
149                         return GetWebConfigFileName (dir);
150                 }
151                 
152                 public virtual string GetStreamNameForConfigSource (string streamName, string configSource)
153                 {
154                         throw new NotImplementedException ();
155                 }
156                 
157                 public virtual object GetStreamVersion (string streamName)
158                 {
159                         throw new NotImplementedException ();
160                 }
161                 
162                 public virtual IDisposable Impersonate ()
163                 {
164                         throw new NotImplementedException ();
165                 }
166                 
167                 public virtual void Init (IInternalConfigRoot root, params object[] hostInitParams)
168                 {
169                 }
170                 
171                 public virtual void InitForConfiguration (ref string locationSubPath, out string configPath,
172                                                           out string locationConfigPath, IInternalConfigRoot root,
173                                                           params object[] hostInitConfigurationParams)
174                 {
175                         string fullPath = (string) hostInitConfigurationParams [1];
176                         map = (WebConfigurationFileMap) hostInitConfigurationParams [0];
177                         bool inAnotherApp = (bool) hostInitConfigurationParams [7];
178
179                         if (inAnotherApp)
180                                 appVirtualPath = fullPath;
181                         else
182                                 appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
183                         
184                         if (locationSubPath == MachineWebPath) {
185                                 locationSubPath = MachinePath;
186                                 configPath = MachineWebPath;
187                                 locationConfigPath = null;
188                         } else if (locationSubPath == MachinePath) {
189                                 locationSubPath = null;
190                                 configPath = MachinePath;
191                                 locationConfigPath = null;
192                         } else {
193                                 int i;
194                                 if (locationSubPath == null) {
195                                         configPath = fullPath;
196                                         if (configPath.Length > 1)
197                                                 configPath = VirtualPathUtility.RemoveTrailingSlash (configPath);
198                                 } else
199                                         configPath = locationSubPath;
200                                 
201                                 if (configPath == HttpRuntime.AppDomainAppVirtualPath || configPath == "/")
202                                         i = -1;
203                                 else
204                                         i = configPath.LastIndexOf ("/");
205
206                                 if (i != -1) {
207                                         locationConfigPath = configPath.Substring (i+1);
208                                         
209                                         if (i == 0)
210                                                 locationSubPath = "/";
211                                         else
212                                                 locationSubPath = fullPath.Substring (0, i);
213                                 } else {
214                                         locationSubPath = MachineWebPath;
215                                         locationConfigPath = null;
216                                 }
217                         }
218                 }
219                 
220                 public string MapPath (string virtualPath)
221                 {
222                         if (!String.IsNullOrEmpty (virtualPath)) {
223                                 if (virtualPath.StartsWith (System.Web.Compilation.BuildManager.FAKE_VIRTUAL_PATH_PREFIX, StringComparison.Ordinal))
224                                         return HttpRuntime.AppDomainAppPath;
225                         }
226                         
227                         if (map != null)
228                                 return MapPathFromMapper (virtualPath);
229                         else if (HttpContext.Current != null && HttpContext.Current.Request != null)
230                                 return HttpContext.Current.Request.MapPath (virtualPath);
231                         else if (HttpRuntime.AppDomainAppVirtualPath != null &&
232                                  virtualPath.StartsWith (HttpRuntime.AppDomainAppVirtualPath)) {
233                                 if (virtualPath == HttpRuntime.AppDomainAppVirtualPath)
234                                         return HttpRuntime.AppDomainAppPath;
235                                 return UrlUtils.Combine (HttpRuntime.AppDomainAppPath,
236                                                          virtualPath.Substring (HttpRuntime.AppDomainAppVirtualPath.Length));
237                         }
238                         
239                         return virtualPath;
240                 }
241                 
242                 public string NormalizeVirtualPath (string virtualPath)
243                 {
244                         if (virtualPath == null || virtualPath.Length == 0)
245                                 virtualPath = ".";
246                         else
247                                 virtualPath = virtualPath.Trim ();
248
249                         if (virtualPath [0] == '~' && virtualPath.Length > 2 && virtualPath [1] == '/')
250                                 virtualPath = virtualPath.Substring (1);
251                                 
252                         if (System.IO.Path.DirectorySeparatorChar != '/')
253                                 virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
254
255                         if (UrlUtils.IsRooted (virtualPath)) {
256                                 virtualPath = UrlUtils.Canonic (virtualPath);
257                         } else {
258                                 if (map.VirtualDirectories.Count > 0) {
259                                         string root = map.VirtualDirectories [0].VirtualDirectory;
260                                         virtualPath = UrlUtils.Combine (root, virtualPath);
261                                         virtualPath = UrlUtils.Canonic (virtualPath);
262                                 }
263                         }
264                         return virtualPath;
265                 }
266
267                 public string MapPathFromMapper (string virtualPath)
268                 {
269                         string path = NormalizeVirtualPath (virtualPath);
270                         
271                         foreach (VirtualDirectoryMapping mapping in map.VirtualDirectories) {
272                                 if (path.StartsWith (mapping.VirtualDirectory)) {
273                                         int i = mapping.VirtualDirectory.Length;
274                                         if (path.Length == i) {
275                                                 return mapping.PhysicalDirectory;
276                                         }
277                                         else if (path [i] == '/') {
278                                                 string pathPart = path.Substring (i + 1).Replace ('/', Path.DirectorySeparatorChar);
279                                                 return Path.Combine (mapping.PhysicalDirectory, pathPart);
280                                         }
281                                 }
282                         }
283                         throw new HttpException ("Invalid virtual directory: " + virtualPath);
284                 }
285
286                 internal static string GetWebConfigFileName (string dir)
287                 {
288                         AppDomain domain = AppDomain.CurrentDomain;
289                         bool hosted = (domain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
290
291                         if (hosted)
292                                 return ApplicationHost.FindWebConfig (dir);
293                         else {
294                                 Assembly asm = Assembly.GetEntryAssembly () ?? Assembly.GetCallingAssembly ();
295                                 string name = Path.GetFileName (asm.Location);
296                                 string[] fileNames = new string[] {name + ".config", name + ".Config"};
297                                 string appDir = domain.BaseDirectory;
298                                 string file;
299
300                                 foreach (string fn in fileNames) {
301                                         file = Path.Combine (appDir, fn);
302                                         if (File.Exists (file))
303                                                 return file;
304                                 }
305                         }
306                         return null;
307                 }
308                 public virtual bool IsAboveApplication (string configPath)
309                 {
310                         return !configPath.Contains (HttpRuntime.AppDomainAppPath);
311                 }
312                 
313                 public virtual bool IsConfigRecordRequired (string configPath)
314                 {
315                         throw new NotImplementedException ();
316                 }
317                 
318                 public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
319                                                          ConfigurationAllowExeDefinition allowExeDefinition)
320                 {
321                         switch (allowDefinition) {
322                                 case ConfigurationAllowDefinition.MachineOnly:
323                                         return configPath == MachinePath || configPath == MachineWebPath;
324                                 case ConfigurationAllowDefinition.MachineToWebRoot:
325                                 case ConfigurationAllowDefinition.MachineToApplication:
326                                         if (String.IsNullOrEmpty (configPath))
327                                                 return true;
328                                         string normalized;
329
330                                         if (VirtualPathUtility.IsRooted (configPath))
331                                                 normalized = VirtualPathUtility.Normalize (configPath);
332                                         else
333                                                 normalized = configPath;
334                                         
335                                         if ((String.Compare (normalized, MachinePath, StringComparison.Ordinal) == 0) ||
336                                                 (String.Compare (normalized, MachineWebPath, StringComparison.Ordinal) == 0))
337                                                         return true;
338                                 
339                                         if ((String.Compare (normalized, appVirtualPath) != 0))
340                                                 return IsApplication (normalized);
341                                 
342                                         return true;
343                                 default:
344                                         return true;
345                         }
346                 }
347                 
348                 [MonoTODO("Should return false in case strPath points to the root of an application.")]
349                 internal bool IsApplication(string strPath)
350                 {
351                         return true;
352                 }
353                 
354                 public virtual bool IsFile (string streamName)
355                 {
356                         throw new NotImplementedException ();
357                 }
358                 
359                 public virtual bool IsLocationApplicable (string configPath)
360                 {
361                         throw new NotImplementedException ();
362                 }
363                 
364                 public virtual Stream OpenStreamForRead (string streamName)
365                 {
366                         if (!File.Exists (streamName)) {
367                                 return null;
368                         }
369                                 
370                         return new FileStream (streamName, FileMode.Open, FileAccess.Read);
371                 }
372
373                 [MonoTODO ("Not implemented")]
374                 public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
375                 {
376                         throw new NotImplementedException ();
377                 }
378
379                 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
380                 {
381                         if (!IsAboveApplication (streamName))
382                                 WebConfigurationManager.SuppressAppReload (true);
383
384                         return new FileStream (streamName, FileMode.Create, FileAccess.Write);
385                 }
386
387                 [MonoTODO ("Not implemented")]
388                 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
389                                                           bool assertPermissions)
390                 {
391                         throw new NotImplementedException ();
392                 }
393                 
394                 public virtual bool PrefetchAll (string configPath, string streamName)
395                 {
396                         throw new NotImplementedException ();
397                 }
398                 
399                 public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
400                 {
401                         throw new NotImplementedException ();
402                 }
403
404                 [MonoTODO ("Not implemented")]
405                 public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
406                 {
407                         throw new NotImplementedException ();
408                 }
409
410                 public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
411                 {                       
412                         throw new NotImplementedException ();
413                 }
414                 
415                 public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
416                 {
417                         throw new NotImplementedException ();
418                 }
419                 
420                 public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
421                                                              ConfigurationAllowExeDefinition allowExeDefinition,
422                                                              IConfigErrorInfo errorInfo)
423                 {
424                         if (!IsDefinitionAllowed (configPath, allowDefinition, allowExeDefinition))
425                                 throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
426                 }
427                 
428                 public virtual void WriteCompleted (string streamName, bool success, object writeContext)
429                 {
430                         WriteCompleted (streamName, success, writeContext, false);
431                 }               
432
433                 public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
434                 {
435                         // There are probably other things to be done here, but for the moment we
436                         // just mark the completed write as one that should not cause application
437                         // reload. Note that it might already be too late for suppression, since the
438                         // FileSystemWatcher monitor might have already delivered the
439                         // notification. If the stream has been open using OpenStreamForWrite then
440                         // we're safe, though.
441
442                         if (!IsAboveApplication (streamName))
443                                 WebConfigurationManager.SuppressAppReload (true);
444                 }
445
446                 public virtual bool SupportsChangeNotifications {
447                         get { return false; }
448                 }
449                 
450                 public virtual bool SupportsLocation {
451                         get { return false; }
452                 }
453                 
454                 public virtual bool SupportsPath {
455                         get { return false; }
456                 }
457                 
458                 public virtual bool SupportsRefresh {
459                         get { return false; }
460                 }
461
462                 [MonoTODO("Always returns false")]
463                 public virtual bool IsRemote {
464                         get { return false; }
465                 }
466
467                 [MonoTODO ("Not implemented")]
468                 public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
469                 {
470                         throw new NotImplementedException ();
471                 }
472
473                 [MonoTODO ("Not implemented")]
474                 public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
475                 {
476                         throw new NotImplementedException ();
477                 }
478
479                 [MonoTODO ("Not implemented")]
480                 public virtual bool IsSecondaryRoot (string configPath)
481                 {
482                         throw new NotImplementedException ();
483                 }
484
485                 [MonoTODO ("Not implemented")]
486                 public virtual bool IsTrustedConfigPath (string configPath)
487                 {
488                         throw new NotImplementedException ();
489                 }
490         }
491 }
492