Merge pull request #205 from m3rlinez/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 #if NET_2_0
31
32 using System;
33 using System.Collections;
34 using System.IO;
35 using System.Security;
36 using System.Configuration;
37 using System.Configuration.Internal;
38 using System.Web.Hosting;
39 using System.Web.Util;
40 using System.Reflection;
41
42 /*
43  * this class needs to be rewritten to support usage of the
44  * IRemoteWebConfigurationHostServer interface.  Once that's done, we
45  * need an implementation of that interface that talks (through a web
46  * service?) to a remote site..
47  *
48  * for now, though, just implement it as we do
49  * System.Configuration.InternalConfigurationHost, i.e. the local
50  * case.
51  */
52 namespace System.Web.Configuration
53 {
54         class WebConfigurationHost: IInternalConfigHost
55         {
56                 WebConfigurationFileMap map;
57                 const string MachinePath = ":machine:";
58                 const string MachineWebPath = ":web:";
59
60                 string appVirtualPath;
61                 
62                 public virtual object CreateConfigurationContext (string configPath, string locationSubPath)
63                 {
64                         return new WebContext (WebApplicationLevel.AtApplication /* XXX */,
65                                                "" /* site XXX */,
66                                                "" /* application path XXX */,
67                                                configPath,
68                                                locationSubPath);
69                 }
70                 
71                 public virtual object CreateDeprecatedConfigContext (string configPath)
72                 {
73                         return new HttpConfigurationContext(configPath);
74                 }
75                 
76                 public virtual string DecryptSection (string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
77                 {
78                         if (protectedSection == null)
79                                 throw new ArgumentNullException ("protectedSection");
80
81                         return protectedSection.EncryptSection (encryptedXml, protectionProvider);
82                 }
83                 
84                 public virtual void DeleteStream (string streamName)
85                 {
86                         File.Delete (streamName);
87                 }
88                 
89                 public virtual string EncryptSection (string clearXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)
90                 {
91                         if (protectedSection == null)
92                                 throw new ArgumentNullException ("protectedSection");
93
94                         return protectedSection.EncryptSection (clearXml, protectionProvider);
95                 }
96                 
97                 public virtual string GetConfigPathFromLocationSubPath (string configPath, string locationSubPath)
98                 {
99                         if (!String.IsNullOrEmpty (locationSubPath) && !String.IsNullOrEmpty (configPath)) {
100                                 string relConfigPath = configPath.Length == 1 ? null : configPath.Substring (1) + "/";
101                                 if (relConfigPath != null && locationSubPath.StartsWith (relConfigPath, StringComparison.Ordinal))
102                                         locationSubPath = locationSubPath.Substring (relConfigPath.Length);
103                         }
104                         
105                         string ret = configPath + "/" + locationSubPath;
106                         if (!String.IsNullOrEmpty (ret) && ret [0] == '/')
107                                 return ret.Substring (1);
108                         
109                         return ret;
110                 }
111                 
112                 public virtual Type GetConfigType (string typeName, bool throwOnError)
113                 {
114                         Type type = HttpApplication.LoadType (typeName);
115                         if (type == null && throwOnError)
116                                 throw new ConfigurationErrorsException ("Type not found: '" + typeName + "'");
117                         return type;
118                 }
119                 
120                 public virtual string GetConfigTypeName (Type t)
121                 {
122                         return t.AssemblyQualifiedName;
123                 }
124                 
125                 public virtual void GetRestrictedPermissions (IInternalConfigRecord configRecord, out PermissionSet permissionSet,
126                                                               out bool isHostReady)
127                 {
128                         throw new NotImplementedException ();
129                 }
130                 
131                 public virtual string GetStreamName (string configPath)
132                 {
133                         if (configPath == MachinePath) {
134                                 if (map == null)
135                                         return System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
136                                 else
137                                         return map.MachineConfigFilename;
138                         } else if (configPath == MachineWebPath) {
139                                 string mdir;
140
141                                 if (map == null)
142 #if TARGET_J2EE
143                                 {
144                                         // check META-INF/web.config exists
145                                         java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
146                                         if (cl == null)
147                                                 return null;
148                                         java.io.InputStream wcs = cl.getResourceAsStream ("META-INF/web.config");
149                                         if (wcs == null)
150                                                 return null;
151
152                                         wcs.close ();
153
154                                         return "/META-INF/web.config";
155                                 }
156 #else
157                                         mdir = Path.GetDirectoryName (System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile);
158 #endif
159                                 else
160                                         mdir = Path.GetDirectoryName (map.MachineConfigFilename);
161
162                                 return GetWebConfigFileName (mdir);
163                         }
164                         
165                         string dir = MapPath (configPath);
166                         return GetWebConfigFileName (dir);
167                 }
168                 
169                 public virtual string GetStreamNameForConfigSource (string streamName, string configSource)
170                 {
171                         throw new NotImplementedException ();
172                 }
173                 
174                 public virtual object GetStreamVersion (string streamName)
175                 {
176                         throw new NotImplementedException ();
177                 }
178                 
179                 public virtual IDisposable Impersonate ()
180                 {
181                         throw new NotImplementedException ();
182                 }
183                 
184                 public virtual void Init (IInternalConfigRoot root, params object[] hostInitParams)
185                 {
186                 }
187                 
188                 public virtual void InitForConfiguration (ref string locationSubPath, out string configPath,
189                                                           out string locationConfigPath, IInternalConfigRoot root,
190                                                           params object[] hostInitConfigurationParams)
191                 {
192                         string fullPath = (string) hostInitConfigurationParams [1];
193                         map = (WebConfigurationFileMap) hostInitConfigurationParams [0];
194                         bool inAnotherApp = (bool) hostInitConfigurationParams [7];
195
196                         if (inAnotherApp)
197                                 appVirtualPath = fullPath;
198                         else
199                                 appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
200                         
201                         if (locationSubPath == MachineWebPath) {
202                                 locationSubPath = MachinePath;
203                                 configPath = MachineWebPath;
204                                 locationConfigPath = null;
205                         } else if (locationSubPath == MachinePath) {
206                                 locationSubPath = null;
207                                 configPath = MachinePath;
208                                 locationConfigPath = null;
209                         } else {
210                                 int i;
211                                 if (locationSubPath == null) {
212                                         configPath = fullPath;
213                                         if (configPath.Length > 1)
214                                                 configPath = VirtualPathUtility.RemoveTrailingSlash (configPath);
215                                 } else
216                                         configPath = locationSubPath;
217                                 
218                                 if (configPath == HttpRuntime.AppDomainAppVirtualPath || configPath == "/")
219                                         i = -1;
220                                 else
221                                         i = configPath.LastIndexOf ("/");
222
223                                 if (i != -1) {
224                                         locationConfigPath = configPath.Substring (i+1);
225                                         
226                                         if (i == 0)
227                                                 locationSubPath = "/";
228                                         else
229                                                 locationSubPath = fullPath.Substring (0, i);
230                                 } else {
231                                         locationSubPath = MachineWebPath;
232                                         locationConfigPath = null;
233                                 }
234                         }
235                 }
236                 
237                 public string MapPath (string virtualPath)
238                 {
239                         if (!String.IsNullOrEmpty (virtualPath)) {
240                                 if (virtualPath.StartsWith (System.Web.Compilation.BuildManager.FAKE_VIRTUAL_PATH_PREFIX, StringComparison.Ordinal))
241                                         return HttpRuntime.AppDomainAppPath;
242                         }
243                         
244                         if (map != null)
245                                 return MapPathFromMapper (virtualPath);
246                         else if (HttpContext.Current != null && HttpContext.Current.Request != null)
247                                 return HttpContext.Current.Request.MapPath (virtualPath);
248                         else if (HttpRuntime.AppDomainAppVirtualPath != null &&
249                                  virtualPath.StartsWith (HttpRuntime.AppDomainAppVirtualPath)) {
250                                 if (virtualPath == HttpRuntime.AppDomainAppVirtualPath)
251                                         return HttpRuntime.AppDomainAppPath;
252                                 return UrlUtils.Combine (HttpRuntime.AppDomainAppPath,
253                                                          virtualPath.Substring (HttpRuntime.AppDomainAppVirtualPath.Length));
254                         }
255                         
256                         return virtualPath;
257                 }
258                 
259                 public string NormalizeVirtualPath (string virtualPath)
260                 {
261                         if (virtualPath == null || virtualPath.Length == 0)
262                                 virtualPath = ".";
263                         else
264                                 virtualPath = virtualPath.Trim ();
265
266                         if (virtualPath [0] == '~' && virtualPath.Length > 2 && virtualPath [1] == '/')
267                                 virtualPath = virtualPath.Substring (1);
268                                 
269                         if (System.IO.Path.DirectorySeparatorChar != '/')
270                                 virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
271
272                         if (UrlUtils.IsRooted (virtualPath)) {
273                                 virtualPath = UrlUtils.Canonic (virtualPath);
274                         } else {
275                                 if (map.VirtualDirectories.Count > 0) {
276                                         string root = map.VirtualDirectories [0].VirtualDirectory;
277                                         virtualPath = UrlUtils.Combine (root, virtualPath);
278                                         virtualPath = UrlUtils.Canonic (virtualPath);
279                                 }
280                         }
281                         return virtualPath;
282                 }
283
284                 public string MapPathFromMapper (string virtualPath)
285                 {
286                         string path = NormalizeVirtualPath (virtualPath);
287                         
288                         foreach (VirtualDirectoryMapping mapping in map.VirtualDirectories) {
289                                 if (path.StartsWith (mapping.VirtualDirectory)) {
290                                         int i = mapping.VirtualDirectory.Length;
291                                         if (path.Length == i) {
292                                                 return mapping.PhysicalDirectory;
293                                         }
294                                         else if (path [i] == '/') {
295                                                 string pathPart = path.Substring (i + 1).Replace ('/', Path.DirectorySeparatorChar);
296                                                 return Path.Combine (mapping.PhysicalDirectory, pathPart);
297                                         }
298                                 }
299                         }
300                         throw new HttpException ("Invalid virtual directory: " + virtualPath);
301                 }
302
303                 internal static string GetWebConfigFileName (string dir)
304                 {
305 #if TARGET_J2EE
306                         DirectoryInfo d = GetCaseSensitiveExistingDirectory (new DirectoryInfo (dir));
307                         if (d == null)
308                                 return null;
309
310                         FileInfo file = (FileInfo) FindByName ("web.config", d.GetFiles ("W*"));
311                         if (file == null)
312                                 file = (FileInfo) FindByName ("web.config", d.GetFiles ("w*"));
313
314                         if (file != null)
315                                 return file.FullName;
316 #else
317                         AppDomain domain = AppDomain.CurrentDomain;
318                         bool hosted = (domain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
319
320                         if (hosted)
321                                 return ApplicationHost.FindWebConfig (dir);
322                         else {
323                                 Assembly asm = Assembly.GetEntryAssembly () ?? Assembly.GetCallingAssembly ();
324                                 string name = Path.GetFileName (asm.Location);
325                                 string[] fileNames = new string[] {name + ".config", name + ".Config"};
326                                 string appDir = domain.BaseDirectory;
327                                 string file;
328
329                                 foreach (string fn in fileNames) {
330                                         file = Path.Combine (appDir, fn);
331                                         if (File.Exists (file))
332                                                 return file;
333                                 }
334                         }
335 #endif                  
336                         return null;
337                 }
338 #if TARGET_J2EE
339                 static DirectoryInfo GetCaseSensitiveExistingDirectory (DirectoryInfo dir) {
340                         if (dir == null)
341                                 return null;
342                         if (dir.Exists)
343                                 return dir;
344
345                         DirectoryInfo parent = GetCaseSensitiveExistingDirectory (dir.Parent);
346                         if (parent == null)
347                                 return null;
348
349                         return (DirectoryInfo) FindByName (dir.Name, parent.GetDirectories ());
350                 }
351                 
352                 static FileSystemInfo FindByName (string name, FileSystemInfo [] infos)
353                 {
354                         for (int i = 0; i < infos.Length; i++) {
355                                 if (String.Compare (name, infos [i].Name, StringComparison.OrdinalIgnoreCase) == 0)
356                                         return infos [i];
357                         }
358                         return null;
359                 }
360 #endif
361                 public virtual bool IsAboveApplication (string configPath)
362                 {
363                         return !configPath.Contains (HttpRuntime.AppDomainAppPath);
364                 }
365                 
366                 public virtual bool IsConfigRecordRequired (string configPath)
367                 {
368                         throw new NotImplementedException ();
369                 }
370                 
371                 public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
372                                                          ConfigurationAllowExeDefinition allowExeDefinition)
373                 {
374                         switch (allowDefinition) {
375                                 case ConfigurationAllowDefinition.MachineOnly:
376                                         return configPath == MachinePath || configPath == MachineWebPath;
377                                 case ConfigurationAllowDefinition.MachineToWebRoot:
378                                 case ConfigurationAllowDefinition.MachineToApplication:
379                                         if (String.IsNullOrEmpty (configPath))
380                                                 return true;
381                                         string normalized;
382
383                                         if (VirtualPathUtility.IsRooted (configPath))
384                                                 normalized = VirtualPathUtility.Normalize (configPath);
385                                         else
386                                                 normalized = configPath;
387                                         
388                                         if ((String.Compare (normalized, MachinePath, StringComparison.Ordinal) == 0) ||
389                                                 (String.Compare (normalized, MachineWebPath, StringComparison.Ordinal) == 0))
390                                                         return true;
391                                 
392                                         if ((String.Compare (normalized, appVirtualPath) != 0))
393                                                 return IsApplication (normalized);
394                                 
395                                         return true;
396                                 default:
397                                         return true;
398                         }
399                 }
400                 
401                 [MonoTODO("Should return false in case strPath points to the root of an application.")]
402                 internal bool IsApplication(string strPath)
403                 {
404                         return true;
405                 }
406                 
407                 public virtual bool IsFile (string streamName)
408                 {
409                         throw new NotImplementedException ();
410                 }
411                 
412                 public virtual bool IsLocationApplicable (string configPath)
413                 {
414                         throw new NotImplementedException ();
415                 }
416                 
417                 public virtual Stream OpenStreamForRead (string streamName)
418                 {
419                         if (!File.Exists (streamName)) {
420 #if TARGET_J2EE
421                                 if (streamName != null && (streamName.EndsWith ("machine.config") ||
422                                                            streamName.EndsWith ("web.config"))) {
423                                         if (streamName.StartsWith ("/"))
424                                                 streamName = streamName.Substring (1);
425                                         java.lang.ClassLoader cl = (java.lang.ClassLoader) AppDomain.CurrentDomain.GetData ("GH_ContextClassLoader");
426                                         if (cl != null) {
427                                                 java.io.InputStream inputStream = cl.getResourceAsStream (streamName);
428                                                 return new System.Web.J2EE.J2EEUtils.InputStreamWrapper (inputStream);
429                                         }
430                                 }
431 #endif
432                                 return null;
433                         }
434                                 
435                         return new FileStream (streamName, FileMode.Open, FileAccess.Read);
436                 }
437
438                 [MonoTODO ("Not implemented")]
439                 public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
440                 {
441                         throw new NotImplementedException ();
442                 }
443
444                 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
445                 {
446                         if (!IsAboveApplication (streamName))
447                                 WebConfigurationManager.SuppressAppReload (true);
448
449                         return new FileStream (streamName, FileMode.Create, FileAccess.Write);
450                 }
451
452                 [MonoTODO ("Not implemented")]
453                 public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
454                                                           bool assertPermissions)
455                 {
456                         throw new NotImplementedException ();
457                 }
458                 
459                 public virtual bool PrefetchAll (string configPath, string streamName)
460                 {
461                         throw new NotImplementedException ();
462                 }
463                 
464                 public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
465                 {
466                         throw new NotImplementedException ();
467                 }
468
469                 [MonoTODO ("Not implemented")]
470                 public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
471                 {
472                         throw new NotImplementedException ();
473                 }
474
475                 public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
476                 {                       
477                         throw new NotImplementedException ();
478                 }
479                 
480                 public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
481                 {
482                         throw new NotImplementedException ();
483                 }
484                 
485                 public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
486                                                              ConfigurationAllowExeDefinition allowExeDefinition,
487                                                              IConfigErrorInfo errorInfo)
488                 {
489                         if (!IsDefinitionAllowed (configPath, allowDefinition, allowExeDefinition))
490                                 throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
491                 }
492                 
493                 public virtual void WriteCompleted (string streamName, bool success, object writeContext)
494                 {
495                         WriteCompleted (streamName, success, writeContext, false);
496                 }               
497
498                 public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
499                 {
500                         // There are probably other things to be done here, but for the moment we
501                         // just mark the completed write as one that should not cause application
502                         // reload. Note that it might already be too late for suppression, since the
503                         // FileSystemWatcher monitor might have already delivered the
504                         // notification. If the stream has been open using OpenStreamForWrite then
505                         // we're safe, though.
506
507                         if (!IsAboveApplication (streamName))
508                                 WebConfigurationManager.SuppressAppReload (true);
509                 }
510
511                 public virtual bool SupportsChangeNotifications {
512                         get { return false; }
513                 }
514                 
515                 public virtual bool SupportsLocation {
516                         get { return false; }
517                 }
518                 
519                 public virtual bool SupportsPath {
520                         get { return false; }
521                 }
522                 
523                 public virtual bool SupportsRefresh {
524                         get { return false; }
525                 }
526
527                 [MonoTODO("Always returns false")]
528                 public virtual bool IsRemote {
529                         get { return false; }
530                 }
531
532                 [MonoTODO ("Not implemented")]
533                 public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
534                 {
535                         throw new NotImplementedException ();
536                 }
537
538                 [MonoTODO ("Not implemented")]
539                 public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
540                 {
541                         throw new NotImplementedException ();
542                 }
543
544                 [MonoTODO ("Not implemented")]
545                 public virtual bool IsSecondaryRoot (string configPath)
546                 {
547                         throw new NotImplementedException ();
548                 }
549
550                 [MonoTODO ("Not implemented")]
551                 public virtual bool IsTrustedConfigPath (string configPath)
552                 {
553                         throw new NotImplementedException ();
554                 }
555         }
556 }
557
558 #endif