2012-07-28 00:13:06 +06:00
using System ;
using System.Collections.Generic ;
using System.Configuration ;
using System.Web ;
using System.Web.Configuration ;
using System.Xml ;
using Umbraco.Core.IO ;
2012-08-06 22:40:06 +06:00
using Umbraco.Core.Logging ;
2012-07-28 00:13:06 +06:00
namespace Umbraco.Core.Configuration
{
//NOTE: Do not expose this class ever until we cleanup all configuration including removal of static classes, etc...
// we have this two tasks logged:
// http://issues.umbraco.org/issue/U4-58
// http://issues.umbraco.org/issue/U4-115
//TODO: Re-enable Logging!!!!
2012-08-09 07:41:13 +06:00
//TODO: Remove checks for if HttpContext is null, why are we doing this? we should be checking if the config setting
// can be read and if not then return the default.
2012-07-28 00:13:06 +06:00
/// <summary>
/// The GlobalSettings Class contains general settings information for the entire Umbraco instance based on information from web.config appsettings
/// </summary>
internal class GlobalSettings
{
2012-08-06 22:40:06 +06:00
private static HttpContextBase _customHttpContext ;
/// <summary>
/// Gets/sets the HttpContext object, this is generally used for unit testing. By default this will
/// use the HttpContext.Current
/// </summary>
internal static HttpContextBase HttpContext
{
get
{
if ( _customHttpContext = = null & & System . Web . HttpContext . Current ! = null )
{
//return the current HttpContxt, do NOT store this in the _customHttpContext field
//as it will persist across reqeusts!
return new HttpContextWrapper ( System . Web . HttpContext . Current ) ;
}
if ( _customHttpContext = = null & & System . Web . HttpContext . Current = = null )
{
//throw new NullReferenceException("The HttpContext property has not been set or the object execution is not running inside of an HttpContext");
//NOTE: We should throw an exception here but the legacy code checks for null so we need to stick witht he legacy code for now.
return null ;
}
return _customHttpContext ;
}
set { _customHttpContext = value ; }
}
2012-07-28 00:13:06 +06:00
#region Private static fields
// CURRENT UMBRACO VERSION ID
2012-08-07 21:42:11 +06:00
private const string CurrentUmbracoVersion = "4.9.0" ;
2012-07-28 00:13:06 +06:00
private static string _reservedUrlsCache ;
private static string _reservedPathsCache ;
private static StartsWithContainer _reservedList = new StartsWithContainer ( ) ;
#endregion
/// <summary>
/// Gets the reserved urls from web.config.
/// </summary>
/// <value>The reserved urls.</value>
public static string ReservedUrls
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoReservedUrls" ] ;
return String . Empty ;
}
}
/// <summary>
/// Gets the reserved paths from web.config
/// </summary>
/// <value>The reserved paths.</value>
public static string ReservedPaths
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoReservedPaths" ] ;
return String . Empty ;
}
}
/// <summary>
/// Gets the name of the content XML file.
/// </summary>
/// <value>The content XML.</value>
public static string ContentXml
{
get
{
try
{
return ConfigurationManager . AppSettings [ "umbracoContentXML" ] ;
}
catch
{
return String . Empty ;
}
}
}
/// <summary>
/// Gets the path to the storage directory (/data by default).
/// </summary>
/// <value>The storage directory.</value>
public static string StorageDirectory
{
get
{
try
{
return ConfigurationManager . AppSettings [ "umbracoStorageDirectory" ] ;
}
catch
{
return String . Empty ;
}
}
}
/// <summary>
/// Gets the path to umbraco's root directory (/umbraco by default).
/// </summary>
/// <value>The path.</value>
public static string Path
{
get
{
try
{
return IOHelper . ResolveUrl ( ConfigurationManager . AppSettings [ "umbracoPath" ] ) ;
}
catch
{
return String . Empty ;
}
}
}
/// <summary>
/// Gets the path to umbraco's client directory (/umbraco_client by default).
/// This is a relative path to the Umbraco Path as it always must exist beside the 'umbraco'
/// folder since the CSS paths to images depend on it.
/// </summary>
/// <value>The path.</value>
public static string ClientPath
{
get
{
return Path + "/../umbraco_client" ;
}
}
/// <summary>
/// Gets the database connection string
/// </summary>
/// <value>The database connection string.</value>
public static string DbDsn
{
get
{
try
{
return ConfigurationManager . AppSettings [ "umbracoDbDSN" ] ;
}
catch
{
return String . Empty ;
}
}
set
{
if ( DbDsn ! = value )
{
SaveSetting ( "umbracoDbDSN" , value ) ;
}
}
}
/// <summary>
/// Gets or sets the configuration status. This will return the version number of the currently installed umbraco instance.
/// </summary>
/// <value>The configuration status.</value>
public static string ConfigurationStatus
{
get
{
try
{
return ConfigurationManager . AppSettings [ "umbracoConfigurationStatus" ] ;
}
catch
{
return String . Empty ;
}
}
set
{
SaveSetting ( "umbracoConfigurationStatus" , value ) ;
}
}
/// <summary>
/// Forces umbraco to be medium trust compatible
/// </summary>
/// <value>If true, umbraco will be medium-trust compatible, no matter what Permission level the server is on.</value>
public static bool UseMediumTrust
{
get
{
try
{
var trustLevel = SystemUtilities . GetCurrentTrustLevel ( ) ;
if ( trustLevel = = AspNetHostingPermissionLevel . High | | trustLevel = = AspNetHostingPermissionLevel . Unrestricted )
return false ;
else
return bool . Parse ( ConfigurationManager . AppSettings [ "umbracoUseMediumTrust" ] ) ;
}
catch
{
return false ;
}
}
}
/// <summary>
/// Saves a setting into the configuration file.
/// </summary>
/// <param name="key">Key of the setting to be saved.</param>
/// <param name="value">Value of the setting to be saved.</param>
internal static void SaveSetting ( string key , string value )
{
var webConfig = new WebConfigurationFileMap ( ) ;
var vDirs = webConfig . VirtualDirectories ;
var vDir = FullpathToRoot ;
foreach ( VirtualDirectoryMapping v in webConfig . VirtualDirectories )
{
if ( v . IsAppRoot )
{
vDir = v . PhysicalDirectory ;
}
}
var doc = new XmlDocument ( ) ;
doc . Load ( String . Concat ( vDir , "web.config" ) ) ;
var root = doc . DocumentElement ;
var setting = doc . SelectSingleNode ( String . Concat ( "//appSettings/add[@key='" , key , "']" ) ) ;
setting . Attributes [ "value" ] . InnerText = value ;
doc . Save ( String . Concat ( vDir , "web.config" ) ) ;
ConfigurationManager . RefreshSection ( "appSettings" ) ;
}
/// <summary>
/// Gets the full path to root.
/// </summary>
/// <value>The fullpath to root.</value>
public static string FullpathToRoot
{
get { return HttpRuntime . AppDomainAppPath ; }
}
/// <summary>
/// Gets a value indicating whether umbraco is running in [debug mode].
/// </summary>
/// <value><c>true</c> if [debug mode]; otherwise, <c>false</c>.</value>
public static bool DebugMode
{
get
{
try
{
return bool . Parse ( ConfigurationManager . AppSettings [ "umbracoDebugMode" ] ) ;
}
catch
{
return false ;
}
}
}
/// <summary>
/// Gets a value indicating whether the current version of umbraco is configured.
/// </summary>
/// <value><c>true</c> if configured; otherwise, <c>false</c>.</value>
public static bool Configured
{
get
{
try
{
string configStatus = ConfigurationStatus ;
string currentVersion = CurrentVersion ;
if ( currentVersion ! = configStatus )
{
//Log.Add(LogTypes.Debug, User.GetUser(0), -1,
// "CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus +
// "'");
}
return ( configStatus = = currentVersion ) ;
}
catch
{
return false ;
}
}
}
/// <summary>
/// Gets the time out in minutes.
/// </summary>
/// <value>The time out in minutes.</value>
public static int TimeOutInMinutes
{
get
{
try
{
return int . Parse ( ConfigurationManager . AppSettings [ "umbracoTimeOutInMinutes" ] ) ;
}
catch
{
return 20 ;
}
}
}
/// <summary>
/// Gets a value indicating whether umbraco uses directory urls.
/// </summary>
/// <value><c>true</c> if umbraco uses directory urls; otherwise, <c>false</c>.</value>
public static bool UseDirectoryUrls
{
get
{
try
{
return bool . Parse ( ConfigurationManager . AppSettings [ "umbracoUseDirectoryUrls" ] ) ;
}
catch
{
return false ;
}
}
}
/// <summary>
/// Returns a string value to determine if umbraco should skip version-checking.
/// </summary>
/// <value>The version check period in days (0 = never).</value>
public static int VersionCheckPeriod
{
get
{
int versionCheckPeriod = 7 ;
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
{
if ( int . TryParse ( ConfigurationManager . AppSettings [ "umbracoVersionCheckPeriod" ] , out versionCheckPeriod ) )
return versionCheckPeriod ;
}
return versionCheckPeriod ;
}
}
/// <summary>
/// Gets the URL forbitten characters.
/// </summary>
/// <value>The URL forbitten characters.</value>
public static string UrlForbittenCharacters
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoUrlForbittenCharacters" ] ;
return "" ;
}
}
/// <summary>
/// Gets the URL space character.
/// </summary>
/// <value>The URL space character.</value>
public static string UrlSpaceCharacter
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoUrlSpaceCharacter" ] ;
return "" ;
}
}
/// <summary>
/// Gets the SMTP server IP-address or hostname.
/// </summary>
/// <value>The SMTP server.</value>
public static string SmtpServer
{
get
{
try
{
var mailSettings = ConfigurationManager . GetSection ( "system.net/mailSettings" ) as System . Net . Configuration . MailSettingsSectionGroup ;
if ( mailSettings ! = null )
return mailSettings . Smtp . Network . Host ;
else
return ConfigurationManager . AppSettings [ "umbracoSmtpServer" ] ;
}
catch
{
return "" ;
}
}
}
/// <summary>
/// Returns a string value to determine if umbraco should disbable xslt extensions
/// </summary>
/// <value><c>"true"</c> if version xslt extensions are disabled, otherwise, <c>"false"</c></value>
public static string DisableXsltExtensions
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoDisableXsltExtensions" ] ;
return "" ;
}
}
/// <summary>
/// Returns a string value to determine if umbraco should use Xhtml editing mode in the wysiwyg editor
/// </summary>
/// <value><c>"true"</c> if Xhtml mode is enable, otherwise, <c>"false"</c></value>
public static string EditXhtmlMode
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoEditXhtmlMode" ] ;
return "" ;
}
}
/// <summary>
/// Gets the default UI language.
/// </summary>
/// <value>The default UI language.</value>
public static string DefaultUILanguage
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoDefaultUILanguage" ] ;
return "" ;
}
}
/// <summary>
/// Gets the profile URL.
/// </summary>
/// <value>The profile URL.</value>
public static string ProfileUrl
{
get
{
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
return ConfigurationManager . AppSettings [ "umbracoProfileUrl" ] ;
return "" ;
}
}
/// <summary>
/// Gets a value indicating whether umbraco should hide top level nodes from generated urls.
/// </summary>
/// <value>
/// <c>true</c> if umbraco hides top level nodes from urls; otherwise, <c>false</c>.
/// </value>
public static bool HideTopLevelNodeFromPath
{
get
{
2012-09-01 08:11:40 +07:00
try
{
return bool . Parse ( ConfigurationManager . AppSettings [ "umbracoHideTopLevelNodeFromPath" ] ) ;
}
catch
{
return false ;
}
2012-07-28 00:13:06 +06:00
}
}
/// <summary>
/// Gets the current version.
/// </summary>
/// <value>The current version.</value>
public static string CurrentVersion
{
get
{
// change this to be hardcoded in the binary
return CurrentUmbracoVersion ;
}
}
/// <summary>
/// Gets the major version number.
/// </summary>
/// <value>The major version number.</value>
public static int VersionMajor
{
get
{
string [ ] version = CurrentVersion . Split ( "." . ToCharArray ( ) ) ;
return int . Parse ( version [ 0 ] ) ;
}
}
/// <summary>
/// Gets the minor version number.
/// </summary>
/// <value>The minor version number.</value>
public static int VersionMinor
{
get
{
string [ ] version = CurrentVersion . Split ( "." . ToCharArray ( ) ) ;
return int . Parse ( version [ 1 ] ) ;
}
}
/// <summary>
/// Gets the patch version number.
/// </summary>
/// <value>The patch version number.</value>
public static int VersionPatch
{
get
{
string [ ] version = CurrentVersion . Split ( "." . ToCharArray ( ) ) ;
return int . Parse ( version [ 2 ] ) ;
}
}
/// <summary>
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string VersionComment
{
get
{
string [ ] version = CurrentVersion . Split ( "." . ToCharArray ( ) ) ;
if ( version . Length > 3 )
return version [ 3 ] ;
else
return "" ;
}
}
/// <summary>
/// Requests the is in umbraco application directory structure.
/// </summary>
/// <param name="context">The context.</param>
/// <returns></returns>
public static bool RequestIsInUmbracoApplication ( HttpContext context )
{
return context . Request . Path . ToLower ( ) . IndexOf ( IOHelper . ResolveUrl ( SystemDirectories . Umbraco ) . ToLower ( ) ) > - 1 ;
}
public static bool RequestIsLiveEditRedirector ( HttpContext context )
{
return context . Request . Path . ToLower ( ) . IndexOf ( SystemDirectories . Umbraco . ToLower ( ) + "/liveediting.aspx" ) > - 1 ;
}
/// <summary>
/// Gets a value indicating whether umbraco should force a secure (https) connection to the backoffice.
/// </summary>
/// <value><c>true</c> if [use SSL]; otherwise, <c>false</c>.</value>
public static bool UseSSL
{
get
{
try
{
return bool . Parse ( ConfigurationManager . AppSettings [ "umbracoUseSSL" ] ) ;
}
catch
{
return false ;
}
}
}
/// <summary>
/// Gets the umbraco license.
/// </summary>
/// <value>The license.</value>
public static string License
{
get
{
string license =
"<A href=\"http://umbraco.org/redir/license\" target=\"_blank\">the open source license MIT</A>. The umbraco UI is freeware licensed under the umbraco license." ;
2012-08-06 22:40:06 +06:00
if ( HttpContext ! = null )
2012-07-28 00:13:06 +06:00
{
var versionDoc = new XmlDocument ( ) ;
var versionReader = new XmlTextReader ( IOHelper . MapPath ( SystemDirectories . Umbraco + "/version.xml" ) ) ;
versionDoc . Load ( versionReader ) ;
versionReader . Close ( ) ;
// check for license
try
{
string licenseUrl =
versionDoc . SelectSingleNode ( "/version/licensing/licenseUrl" ) . FirstChild . Value ;
string licenseValidation =
versionDoc . SelectSingleNode ( "/version/licensing/licenseValidation" ) . FirstChild . Value ;
string licensedTo =
versionDoc . SelectSingleNode ( "/version/licensing/licensedTo" ) . FirstChild . Value ;
if ( licensedTo ! = "" & & licenseUrl ! = "" )
{
license = "umbraco Commercial License<br/><b>Registered to:</b><br/>" +
licensedTo . Replace ( "\n" , "<br/>" ) + "<br/><b>For use with domain:</b><br/>" +
licenseUrl ;
}
}
catch
{
}
}
return license ;
}
}
/// <summary>
/// Developer method to test if configuration settings are loaded properly.
/// </summary>
/// <value><c>true</c> if succesfull; otherwise, <c>false</c>.</value>
internal static bool Test
{
get
2012-08-06 22:40:06 +06:00
{
if ( HttpContext ! = null )
{
HttpContext . Response . Write ( "ContentXML :" + ContentXml + "\n" ) ;
HttpContext . Response . Write ( "DbDSN :" + DbDsn + "\n" ) ;
HttpContext . Response . Write ( "DebugMode :" + DebugMode + "\n" ) ;
HttpContext . Response . Write ( "DefaultUILanguage :" + DefaultUILanguage + "\n" ) ;
HttpContext . Response . Write ( "VersionCheckPeriod :" + VersionCheckPeriod + "\n" ) ;
HttpContext . Response . Write ( "DisableXsltExtensions :" + DisableXsltExtensions + "\n" ) ;
HttpContext . Response . Write ( "EditXhtmlMode :" + EditXhtmlMode + "\n" ) ;
HttpContext . Response . Write ( "HideTopLevelNodeFromPath :" + HideTopLevelNodeFromPath + "\n" ) ;
HttpContext . Response . Write ( "Path :" + Path + "\n" ) ;
HttpContext . Response . Write ( "ProfileUrl :" + ProfileUrl + "\n" ) ;
HttpContext . Response . Write ( "ReservedPaths :" + ReservedPaths + "\n" ) ;
HttpContext . Response . Write ( "ReservedUrls :" + ReservedUrls + "\n" ) ;
HttpContext . Response . Write ( "StorageDirectory :" + StorageDirectory + "\n" ) ;
HttpContext . Response . Write ( "TimeOutInMinutes :" + TimeOutInMinutes + "\n" ) ;
HttpContext . Response . Write ( "UrlForbittenCharacters :" + UrlForbittenCharacters + "\n" ) ;
HttpContext . Response . Write ( "UrlSpaceCharacter :" + UrlSpaceCharacter + "\n" ) ;
HttpContext . Response . Write ( "UseDirectoryUrls :" + UseDirectoryUrls + "\n" ) ;
return true ;
}
2012-07-28 00:13:06 +06:00
return false ;
}
}
/// <summary>
/// Determines whether the specified URL is reserved or is inside a reserved path.
/// </summary>
/// <param name="url">The URL to check.</param>
/// <returns>
/// <c>true</c> if the specified URL is reserved; otherwise, <c>false</c>.
/// </returns>
public static bool IsReservedPathOrUrl ( string url )
{
// check if GlobalSettings.ReservedPaths and GlobalSettings.ReservedUrls are unchanged
if ( ! object . ReferenceEquals ( _reservedPathsCache , GlobalSettings . ReservedPaths )
| | ! object . ReferenceEquals ( _reservedUrlsCache , GlobalSettings . ReservedUrls ) )
{
// store references to strings to determine changes
_reservedPathsCache = GlobalSettings . ReservedPaths ;
_reservedUrlsCache = GlobalSettings . ReservedUrls ;
string _root = SystemDirectories . Root . Trim ( ) . ToLower ( ) ;
// add URLs and paths to a new list
StartsWithContainer _newReservedList = new StartsWithContainer ( ) ;
foreach ( string reservedUrl in _reservedUrlsCache . Split ( new [ ] { "," } , StringSplitOptions . RemoveEmptyEntries ) )
{
//resolves the url to support tilde chars
string reservedUrlTrimmed = IOHelper . ResolveUrl ( reservedUrl ) . Trim ( ) . ToLower ( ) ;
if ( reservedUrlTrimmed . Length > 0 )
_newReservedList . Add ( reservedUrlTrimmed ) ;
}
foreach ( string reservedPath in _reservedPathsCache . Split ( new [ ] { "," } , StringSplitOptions . RemoveEmptyEntries ) )
{
bool trimEnd = ! reservedPath . EndsWith ( "/" ) ;
//resolves the url to support tilde chars
string reservedPathTrimmed = IOHelper . ResolveUrl ( reservedPath ) . Trim ( ) . ToLower ( ) ;
if ( reservedPathTrimmed . Length > 0 )
_newReservedList . Add ( reservedPathTrimmed + ( reservedPathTrimmed . EndsWith ( "/" ) ? "" : "/" ) ) ;
}
// use the new list from now on
_reservedList = _newReservedList ;
}
string res = "" ;
foreach ( string st in _reservedList . _list . Keys )
res + = st + "," ;
2012-08-06 22:40:06 +06:00
LogHelper . Debug < GlobalSettings > ( "reserverd urls: '" + res + "'" ) ;
2012-07-28 00:13:06 +06:00
// return true if url starts with an element of the reserved list
return _reservedList . StartsWith ( url . ToLower ( ) ) ;
}
/// <summary>
/// Structure that checks in logarithmic time
/// if a given string starts with one of the added keys.
/// </summary>
private class StartsWithContainer
{
/// <summary>Internal sorted list of keys.</summary>
public SortedList < string , string > _list
= new SortedList < string , string > ( StartsWithComparator . Instance ) ;
/// <summary>
/// Adds the specified new key.
/// </summary>
/// <param name="newKey">The new key.</param>
public void Add ( string newKey )
{
// if the list already contains an element that begins with newKey, return
if ( String . IsNullOrEmpty ( newKey ) | | StartsWith ( newKey ) )
return ;
// create a new collection, so the old one can still be accessed
SortedList < string , string > newList
= new SortedList < string , string > ( _list . Count + 1 , StartsWithComparator . Instance ) ;
// add only keys that don't already start with newKey, others are unnecessary
foreach ( string key in _list . Keys )
if ( ! key . StartsWith ( newKey ) )
newList . Add ( key , null ) ;
// add the new key
newList . Add ( newKey , null ) ;
// update the list (thread safe, _list was never in incomplete state)
_list = newList ;
}
/// <summary>
/// Checks if the given string starts with any of the added keys.
/// </summary>
/// <param name="target">The target.</param>
/// <returns>true if a key is found that matches the start of target</returns>
/// <remarks>
/// Runs in O(s*log(n)), with n the number of keys and s the length of target.
/// </remarks>
public bool StartsWith ( string target )
{
return _list . ContainsKey ( target ) ;
}
/// <summary>Comparator that tests if a string starts with another.</summary>
/// <remarks>Not a real comparator, since it is not reflexive. (x==y does not imply y==x)</remarks>
private sealed class StartsWithComparator : IComparer < string >
{
/// <summary>Default string comparer.</summary>
private readonly static Comparer < string > _stringComparer = Comparer < string > . Default ;
/// <summary>Gets an instance of the StartsWithComparator.</summary>
public static readonly StartsWithComparator Instance = new StartsWithComparator ( ) ;
/// <summary>
/// Tests if whole begins with all characters of part.
/// </summary>
/// <param name="part">The part.</param>
/// <param name="whole">The whole.</param>
/// <returns>
/// Returns 0 if whole starts with part, otherwise performs standard string comparison.
/// </returns>
public int Compare ( string part , string whole )
{
// let the default string comparer deal with null or when part is not smaller then whole
2012-08-15 01:23:57 +06:00
if ( part = = null | | whole = = null )
return _stringComparer . Compare ( part , whole ) ;
//trim the end '/' of each
part = part . TrimEnd ( '/' ) ;
whole = whole . TrimEnd ( '/' ) ;
if ( part . Length > = whole . Length )
2012-07-28 00:13:06 +06:00
return _stringComparer . Compare ( part , whole ) ;
// loop through all characters that part and whole have in common
int pos = 0 ;
bool match ;
do
{
match = ( part [ pos ] = = whole [ pos ] ) ;
} while ( match & & + + pos < part . Length ) ;
// return result of last comparison
return match ? 0 : ( part [ pos ] < whole [ pos ] ? - 1 : 1 ) ;
}
}
}
}
}