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 
CreateConfigurationContext(string configPath, string locationSubPath)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 
CreateDeprecatedConfigContext(string configPath)70 		public virtual object CreateDeprecatedConfigContext (string configPath)
71 		{
72 			return new HttpConfigurationContext(configPath);
73 		}
74 
DecryptSection(string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)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 
DeleteStream(string streamName)83 		public virtual void DeleteStream (string streamName)
84 		{
85 			File.Delete (streamName);
86 		}
87 
EncryptSection(string clearXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedSection)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 
GetConfigPathFromLocationSubPath(string configPath, string locationSubPath)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 
GetConfigType(string typeName, bool throwOnError)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 
GetConfigTypeName(Type t)119 		public virtual string GetConfigTypeName (Type t)
120 		{
121 			return t.AssemblyQualifiedName;
122 		}
123 
GetRestrictedPermissions(IInternalConfigRecord configRecord, out PermissionSet permissionSet, out bool isHostReady)124 		public virtual void GetRestrictedPermissions (IInternalConfigRecord configRecord, out PermissionSet permissionSet,
125 							      out bool isHostReady)
126 		{
127 			throw new NotImplementedException ();
128 		}
129 
GetStreamName(string configPath)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 
GetStreamNameForConfigSource(string streamName, string configSource)152 		public virtual string GetStreamNameForConfigSource (string streamName, string configSource)
153 		{
154 			throw new NotImplementedException ();
155 		}
156 
GetStreamVersion(string streamName)157 		public virtual object GetStreamVersion (string streamName)
158 		{
159 			throw new NotImplementedException ();
160 		}
161 
Impersonate()162 		public virtual IDisposable Impersonate ()
163 		{
164 			throw new NotImplementedException ();
165 		}
166 
Init(IInternalConfigRoot root, params object[] hostInitParams)167 		public virtual void Init (IInternalConfigRoot root, params object[] hostInitParams)
168 		{
169 		}
170 
InitForConfiguration(ref string locationSubPath, out string configPath, out string locationConfigPath, IInternalConfigRoot root, params object[] hostInitConfigurationParams)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 = false;
178 
179 			if ((hostInitConfigurationParams.Length > 7)
180 				&& (hostInitConfigurationParams[7] is bool))
181 				inAnotherApp = (bool) hostInitConfigurationParams[7];
182 
183 			if (inAnotherApp)
184 				appVirtualPath = fullPath;
185 			else
186 				appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
187 
188 			if (locationSubPath == MachineWebPath) {
189 				locationSubPath = MachinePath;
190 				configPath = MachineWebPath;
191 				locationConfigPath = null;
192 			} else if (locationSubPath == MachinePath) {
193 				locationSubPath = null;
194 				configPath = MachinePath;
195 				locationConfigPath = null;
196 			} else {
197 				int i;
198 				if (locationSubPath == null) {
199 					configPath = fullPath;
200 					if (configPath.Length > 1)
201 						configPath = VirtualPathUtility.RemoveTrailingSlash (configPath);
202 				} else
203 					configPath = locationSubPath;
204 
205 				if (configPath == HttpRuntime.AppDomainAppVirtualPath || configPath == "/")
206 					i = -1;
207 				else
208 					i = configPath.LastIndexOf ("/");
209 
210 				if (i != -1) {
211 					locationConfigPath = configPath.Substring (i+1);
212 
213 					if (i == 0)
214 						locationSubPath = "/";
215 					else
216 						locationSubPath = fullPath.Substring (0, i);
217 				} else {
218 					locationSubPath = MachineWebPath;
219 					locationConfigPath = null;
220 				}
221 			}
222 		}
223 
MapPath(string virtualPath)224 		public string MapPath (string virtualPath)
225 		{
226 			if (!String.IsNullOrEmpty (virtualPath)) {
227 				if (virtualPath.StartsWith (System.Web.Compilation.BuildManager.FAKE_VIRTUAL_PATH_PREFIX, StringComparison.Ordinal))
228 					return HttpRuntime.AppDomainAppPath;
229 			}
230 
231 			if (map != null)
232 				return MapPathFromMapper (virtualPath);
233 			else if (HttpContext.Current != null && HttpContext.Current.Request != null)
234 				return HttpContext.Current.Request.MapPath (virtualPath);
235 			else if (HttpRuntime.AppDomainAppVirtualPath != null &&
236 				 virtualPath.StartsWith (HttpRuntime.AppDomainAppVirtualPath)) {
237 				if (virtualPath == HttpRuntime.AppDomainAppVirtualPath)
238 					return HttpRuntime.AppDomainAppPath;
239 				return UrlUtils.Combine (HttpRuntime.AppDomainAppPath,
240 							 virtualPath.Substring (HttpRuntime.AppDomainAppVirtualPath.Length));
241 			}
242 
243 			return virtualPath;
244 		}
245 
NormalizeVirtualPath(string virtualPath)246 		public string NormalizeVirtualPath (string virtualPath)
247 		{
248 			if (virtualPath == null || virtualPath.Length == 0)
249 				virtualPath = ".";
250 			else
251 				virtualPath = virtualPath.Trim ();
252 
253 			if (virtualPath [0] == '~' && virtualPath.Length > 2 && virtualPath [1] == '/')
254 				virtualPath = virtualPath.Substring (1);
255 
256 			if (System.IO.Path.DirectorySeparatorChar != '/')
257 				virtualPath = virtualPath.Replace (System.IO.Path.DirectorySeparatorChar, '/');
258 
259 			if (UrlUtils.IsRooted (virtualPath)) {
260 				virtualPath = UrlUtils.Canonic (virtualPath);
261 			} else {
262 				if (map.VirtualDirectories.Count > 0) {
263 					string root = map.VirtualDirectories [0].VirtualDirectory;
264 					virtualPath = UrlUtils.Combine (root, virtualPath);
265 					virtualPath = UrlUtils.Canonic (virtualPath);
266 				}
267 			}
268 			return virtualPath;
269 		}
270 
MapPathFromMapper(string virtualPath)271 		public string MapPathFromMapper (string virtualPath)
272 		{
273 			string path = NormalizeVirtualPath (virtualPath);
274 
275 			foreach (VirtualDirectoryMapping mapping in map.VirtualDirectories) {
276 				if (path.StartsWith (mapping.VirtualDirectory)) {
277 					int i = mapping.VirtualDirectory.Length;
278 					if (path.Length == i) {
279 						return mapping.PhysicalDirectory;
280 					}
281 					else if (path [i] == '/') {
282 						string pathPart = path.Substring (i + 1).Replace ('/', Path.DirectorySeparatorChar);
283 						return Path.Combine (mapping.PhysicalDirectory, pathPart);
284 					}
285 				}
286 			}
287 			throw new HttpException ("Invalid virtual directory: " + virtualPath);
288 		}
289 
GetWebConfigFileName(string dir)290 		internal static string GetWebConfigFileName (string dir)
291 		{
292 			AppDomain domain = AppDomain.CurrentDomain;
293 			bool hosted = (domain.GetData (ApplicationHost.MonoHostedDataKey) as string) == "yes";
294 
295 			if (hosted)
296 				return ApplicationHost.FindWebConfig (dir);
297 			else {
298 				Assembly asm = Assembly.GetEntryAssembly () ?? Assembly.GetCallingAssembly ();
299 				string name = Path.GetFileName (asm.Location);
300 				string[] fileNames = new string[] {name + ".config", name + ".Config"};
301 				string appDir = domain.BaseDirectory;
302 				string file;
303 
304 				foreach (string fn in fileNames) {
305 					file = Path.Combine (appDir, fn);
306 					if (File.Exists (file))
307 						return file;
308 				}
309 			}
310 			return null;
311 		}
IsAboveApplication(string configPath)312 		public virtual bool IsAboveApplication (string configPath)
313 		{
314 			return !configPath.Contains (HttpRuntime.AppDomainAppPath);
315 		}
316 
IsConfigRecordRequired(string configPath)317 		public virtual bool IsConfigRecordRequired (string configPath)
318 		{
319 			throw new NotImplementedException ();
320 		}
321 
IsDefinitionAllowed(string configPath, ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition)322 		public virtual bool IsDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
323 							 ConfigurationAllowExeDefinition allowExeDefinition)
324 		{
325 			switch (allowDefinition) {
326 				case ConfigurationAllowDefinition.MachineOnly:
327 					return configPath == MachinePath || configPath == MachineWebPath;
328 				case ConfigurationAllowDefinition.MachineToWebRoot:
329 				case ConfigurationAllowDefinition.MachineToApplication:
330 					if (String.IsNullOrEmpty (configPath))
331 						return true;
332 					string normalized;
333 
334 					if (VirtualPathUtility.IsRooted (configPath))
335 						normalized = VirtualPathUtility.Normalize (configPath);
336 					else
337 						normalized = configPath;
338 
339 					if ((String.Compare (normalized, MachinePath, StringComparison.Ordinal) == 0) ||
340 						(String.Compare (normalized, MachineWebPath, StringComparison.Ordinal) == 0))
341 							return true;
342 
343 					if ((String.Compare (normalized, appVirtualPath) != 0))
344 						return IsApplication (normalized);
345 
346 					return true;
347 				default:
348 					return true;
349 			}
350 		}
351 
352 		[MonoTODO("Should return false in case strPath points to the root of an application.")]
IsApplication(string strPath)353 		internal bool IsApplication(string strPath)
354 		{
355 			return true;
356 		}
357 
IsFile(string streamName)358 		public virtual bool IsFile (string streamName)
359 		{
360 			throw new NotImplementedException ();
361 		}
362 
IsLocationApplicable(string configPath)363 		public virtual bool IsLocationApplicable (string configPath)
364 		{
365 			throw new NotImplementedException ();
366 		}
367 
OpenStreamForRead(string streamName)368 		public virtual Stream OpenStreamForRead (string streamName)
369 		{
370 			if (!File.Exists (streamName)) {
371 				return null;
372 			}
373 
374 			return new FileStream (streamName, FileMode.Open, FileAccess.Read);
375 		}
376 
377 		[MonoTODO ("Not implemented")]
OpenStreamForRead(string streamName, bool assertPermissions)378 		public virtual Stream OpenStreamForRead (string streamName, bool assertPermissions)
379 		{
380 			throw new NotImplementedException ();
381 		}
382 
OpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext)383 		public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext)
384 		{
385 			if (!IsAboveApplication (streamName))
386 				WebConfigurationManager.SuppressAppReload (true);
387 
388 			return new FileStream (streamName, FileMode.Create, FileAccess.Write);
389 		}
390 
391 		[MonoTODO ("Not implemented")]
OpenStreamForWrite(string streamName, string templateStreamName, ref object writeContext, bool assertPermissions)392 		public virtual Stream OpenStreamForWrite (string streamName, string templateStreamName, ref object writeContext,
393 							  bool assertPermissions)
394 		{
395 			throw new NotImplementedException ();
396 		}
397 
PrefetchAll(string configPath, string streamName)398 		public virtual bool PrefetchAll (string configPath, string streamName)
399 		{
400 			throw new NotImplementedException ();
401 		}
402 
PrefetchSection(string sectionGroupName, string sectionName)403 		public virtual bool PrefetchSection (string sectionGroupName, string sectionName)
404 		{
405 			throw new NotImplementedException ();
406 		}
407 
408 		[MonoTODO ("Not implemented")]
RequireCompleteInit(IInternalConfigRecord configRecord)409 		public virtual void RequireCompleteInit (IInternalConfigRecord configRecord)
410 		{
411 			throw new NotImplementedException ();
412 		}
413 
StartMonitoringStreamForChanges(string streamName, StreamChangeCallback callback)414 		public virtual object StartMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
415 		{
416 			throw new NotImplementedException ();
417 		}
418 
StopMonitoringStreamForChanges(string streamName, StreamChangeCallback callback)419 		public virtual void StopMonitoringStreamForChanges (string streamName, StreamChangeCallback callback)
420 		{
421 			throw new NotImplementedException ();
422 		}
423 
VerifyDefinitionAllowed(string configPath, ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition, IConfigErrorInfo errorInfo)424 		public virtual void VerifyDefinitionAllowed (string configPath, ConfigurationAllowDefinition allowDefinition,
425 							     ConfigurationAllowExeDefinition allowExeDefinition,
426 							     IConfigErrorInfo errorInfo)
427 		{
428 			if (!IsDefinitionAllowed (configPath, allowDefinition, allowExeDefinition))
429 				throw new ConfigurationErrorsException ("The section can't be defined in this file (the allowed definition context is '" + allowDefinition + "').", errorInfo.Filename, errorInfo.LineNumber);
430 		}
431 
WriteCompleted(string streamName, bool success, object writeContext)432 		public virtual void WriteCompleted (string streamName, bool success, object writeContext)
433 		{
434 			WriteCompleted (streamName, success, writeContext, false);
435 		}
436 
WriteCompleted(string streamName, bool success, object writeContext, bool assertPermissions)437 		public virtual void WriteCompleted (string streamName, bool success, object writeContext, bool assertPermissions)
438 		{
439 			// There are probably other things to be done here, but for the moment we
440 			// just mark the completed write as one that should not cause application
441 			// reload. Note that it might already be too late for suppression, since the
442 			// FileSystemWatcher monitor might have already delivered the
443 			// notification. If the stream has been open using OpenStreamForWrite then
444 			// we're safe, though.
445 
446 			if (!IsAboveApplication (streamName))
447 				WebConfigurationManager.SuppressAppReload (true);
448 		}
449 
450 		public virtual bool SupportsChangeNotifications {
451 			get { return false; }
452 		}
453 
454 		public virtual bool SupportsLocation {
455 			get { return false; }
456 		}
457 
458 		public virtual bool SupportsPath {
459 			get { return false; }
460 		}
461 
462 		public virtual bool SupportsRefresh {
463 			get { return false; }
464 		}
465 
466 		[MonoTODO("Always returns false")]
467 		public virtual bool IsRemote {
468 			get { return false; }
469 		}
470 
471 		[MonoTODO ("Not implemented")]
IsFullTrustSectionWithoutAptcaAllowed(IInternalConfigRecord configRecord)472 		public virtual bool IsFullTrustSectionWithoutAptcaAllowed (IInternalConfigRecord configRecord)
473 		{
474 			throw new NotImplementedException ();
475 		}
476 
477 		[MonoTODO ("Not implemented")]
IsInitDelayed(IInternalConfigRecord configRecord)478 		public virtual bool IsInitDelayed (IInternalConfigRecord configRecord)
479 		{
480 			throw new NotImplementedException ();
481 		}
482 
483 		[MonoTODO ("Not implemented")]
IsSecondaryRoot(string configPath)484 		public virtual bool IsSecondaryRoot (string configPath)
485 		{
486 			throw new NotImplementedException ();
487 		}
488 
489 		[MonoTODO ("Not implemented")]
IsTrustedConfigPath(string configPath)490 		public virtual bool IsTrustedConfigPath (string configPath)
491 		{
492 			throw new NotImplementedException ();
493 		}
494 	}
495 }
496 
497