2018-06-29 19:52:40 +02:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Security.Cryptography ;
using System.Text ;
using System.Web ;
using System.Xml.Linq ;
using ClientDependency.Core.CompositeFiles.Providers ;
using ClientDependency.Core.Config ;
using Semver ;
using Umbraco.Core.IO ;
using Umbraco.Core.Logging ;
2018-08-29 01:15:46 +10:00
namespace Umbraco.Web.UI.JavaScript
2018-06-29 19:52:40 +02:00
{
/// <summary>
/// A utility class for working with CDF config and cache files - use sparingly!
/// </summary>
public class ClientDependencyConfiguration
{
private readonly ILogger _logger ;
private readonly string _fileName ;
public ClientDependencyConfiguration ( ILogger logger )
{
if ( logger = = null ) throw new ArgumentNullException ( "logger" ) ;
_logger = logger ;
_fileName = IOHelper . MapPath ( string . Format ( "{0}/ClientDependency.config" , SystemDirectories . Config ) ) ;
}
/// <summary>
/// Changes the version number in ClientDependency.config to a hashed value for the version and the DateTime.Day
/// </summary>
/// <param name="version">The <see cref="SemVersion">version</see> of Umbraco we're upgrading to</param>
/// <param name="date">A <see cref="DateTime">date</see> value to use in the hash to prevent this method from updating the version on each startup</param>
/// <param name="dateFormat">Allows the developer to specify the <see cref="string">date precision</see> for the hash (i.e. "yyyyMMdd" would be a precision for the day)</param>
2019-01-26 10:52:19 -05:00
/// <returns>Boolean to indicate successful update of the ClientDependency.config file</returns>
2018-06-29 19:52:40 +02:00
public bool UpdateVersionNumber ( SemVersion version , DateTime date , string dateFormat )
{
var byteContents = Encoding . Unicode . GetBytes ( version + date . ToString ( dateFormat ) ) ;
//This is a way to convert a string to a long
//see https://www.codeproject.com/Articles/34309/Convert-String-to-bit-Integer
//We could much more easily use MD5 which would create us an INT but since that is not compliant with
//hashing standards we have to use SHA
int intHash ;
using ( var hash = SHA256 . Create ( ) )
{
var bytes = hash . ComputeHash ( byteContents ) ;
var longResult = new [ ] { 0 , 8 , 16 , 24 }
. Select ( i = > BitConverter . ToInt64 ( bytes , i ) )
. Aggregate ( ( x , y ) = > x ^ y ) ;
//CDF requires an INT, and although this isn't fail safe, it will work for our purposes. We are not hashing for crypto purposes
//so there could be some collisions with this conversion but it's not a problem for our purposes
//It's also important to note that the long.GetHashCode() implementation in .NET is this: return (int) this ^ (int) (this >> 32);
2019-01-26 10:52:19 -05:00
//which means that this value will not change per AppDomain like some GetHashCode implementations.
2018-06-29 19:52:40 +02:00
intHash = longResult . GetHashCode ( ) ;
}
try
{
var clientDependencyConfigXml = XDocument . Load ( _fileName , LoadOptions . PreserveWhitespace ) ;
if ( clientDependencyConfigXml . Root ! = null )
{
var versionAttribute = clientDependencyConfigXml . Root . Attribute ( "version" ) ;
//Set the new version to the hashcode of now
var oldVersion = versionAttribute . Value ;
var newVersion = Math . Abs ( intHash ) . ToString ( ) ;
//don't update if it's the same version
if ( oldVersion = = newVersion )
return false ;
versionAttribute . SetValue ( newVersion ) ;
clientDependencyConfigXml . Save ( _fileName , SaveOptions . DisableFormatting ) ;
2018-08-14 15:08:32 +01:00
_logger . Info < ClientDependencyConfiguration > ( "Updated version number from {OldVersion} to {NewVersion}" , oldVersion , newVersion ) ;
2018-06-29 19:52:40 +02:00
return true ;
}
}
catch ( Exception ex )
{
2018-08-17 15:41:58 +01:00
_logger . Error < ClientDependencyConfiguration > ( ex , "Couldn't update ClientDependency version number" ) ;
2018-06-29 19:52:40 +02:00
}
return false ;
}
/// <summary>
/// Clears the temporary files stored for the ClientDependency folder
/// </summary>
/// <param name="currentHttpContext"></param>
public bool ClearTempFiles ( HttpContextBase currentHttpContext )
{
var cdfTempDirectories = new HashSet < string > ( ) ;
foreach ( BaseCompositeFileProcessingProvider provider in ClientDependencySettings . Instance
. CompositeFileProcessingProviderCollection )
{
var path = provider . CompositeFilePath . FullName ;
cdfTempDirectories . Add ( path ) ;
}
try
{
2018-08-29 01:15:46 +10:00
var fullPath = currentHttpContext . Server . MapPath ( XmlFileMapper . FileMapDefaultFolder ) ;
2018-06-29 19:52:40 +02:00
if ( fullPath ! = null )
{
cdfTempDirectories . Add ( fullPath ) ;
}
}
catch ( Exception ex )
{
//invalid path format or something... try/catch to be safe
2018-08-17 15:41:58 +01:00
_logger . Error < ClientDependencyConfiguration > ( ex , "Could not get path from ClientDependency.config" ) ;
2018-06-29 19:52:40 +02:00
}
var success = true ;
foreach ( var directory in cdfTempDirectories )
{
var directoryInfo = new DirectoryInfo ( directory ) ;
if ( directoryInfo . Exists = = false )
continue ;
try
{
directoryInfo . Delete ( true ) ;
}
catch ( Exception ex )
{
// Something could be locking the directory or the was another error, making sure we don't break the upgrade installer
2018-08-17 15:41:58 +01:00
_logger . Error < ClientDependencyConfiguration > ( ex , "Could not clear temp files" ) ;
2018-06-29 19:52:40 +02:00
success = false ;
}
}
return success ;
}
}
}