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