# Conflicts: # src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs # src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs # src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs # src/Umbraco.Core/Packaging/ConflictingPackageData.cs # src/Umbraco.Core/Packaging/PackagesRepository.cs # src/Umbraco.Core/PublishedCache/PublishedMember.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs # src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs # src/Umbraco.PublishedCache.NuCache/PublishedContent.cs # src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs # src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs # src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs # src/Umbraco.Web/PublishedContentExtensions.cs # src/Umbraco.Web/Trees/MediaTypeTreeController.cs
155 lines
4.9 KiB
C#
155 lines
4.9 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Umbraco.Core
|
|
{
|
|
/// <summary>
|
|
/// Used to generate a string hash using crypto libraries over multiple objects
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This should be used to generate a reliable hash that survives AppDomain restarts.
|
|
/// This will use the crypto libs to generate the hash and will try to ensure that
|
|
/// strings, etc... are not re-allocated so it's not consuming much memory.
|
|
/// </remarks>
|
|
public class HashGenerator : DisposableObjectSlim
|
|
{
|
|
public HashGenerator()
|
|
{
|
|
_writer = new StreamWriter(_ms, Encoding.Unicode, 1024, leaveOpen: true);
|
|
}
|
|
|
|
private readonly MemoryStream _ms = new MemoryStream();
|
|
private StreamWriter _writer;
|
|
|
|
public void AddInt(int i)
|
|
{
|
|
_writer.Write(i);
|
|
}
|
|
|
|
public void AddLong(long i)
|
|
{
|
|
_writer.Write(i);
|
|
}
|
|
|
|
public void AddObject(object o)
|
|
{
|
|
_writer.Write(o);
|
|
}
|
|
|
|
public void AddDateTime(DateTime d)
|
|
{
|
|
_writer.Write(d.Ticks);
|
|
}
|
|
|
|
public void AddString(string s)
|
|
{
|
|
if (s != null)
|
|
_writer.Write(s);
|
|
}
|
|
|
|
public void AddCaseInsensitiveString(string s)
|
|
{
|
|
//I've tried to no allocate a new string with this which can be done if we use the CompareInfo.GetSortKey method which will create a new
|
|
//byte array that we can use to write to the output, however this also allocates new objects so i really don't think the performance
|
|
//would be much different. In any case, I'll leave this here for reference. We could write the bytes out based on the sort key,
|
|
//this is how we could deal with case insensitivity without allocating another string
|
|
//for reference see: https://stackoverflow.com/a/10452967/694494
|
|
//we could go a step further and s.Normalize() but we're not really dealing with crazy unicode with this class so far.
|
|
|
|
if (s != null)
|
|
_writer.Write(s.ToUpperInvariant());
|
|
}
|
|
|
|
public void AddFileSystemItem(FileSystemInfo f)
|
|
{
|
|
//if it doesn't exist, don't proceed.
|
|
if (f.Exists == false)
|
|
return;
|
|
|
|
AddCaseInsensitiveString(f.FullName);
|
|
AddDateTime(f.CreationTimeUtc);
|
|
AddDateTime(f.LastWriteTimeUtc);
|
|
|
|
//check if it is a file or folder
|
|
var fileInfo = f as FileInfo;
|
|
if (fileInfo != null)
|
|
{
|
|
AddLong(fileInfo.Length);
|
|
}
|
|
|
|
var dirInfo = f as DirectoryInfo;
|
|
if (dirInfo != null)
|
|
{
|
|
foreach (var d in dirInfo.GetFiles())
|
|
{
|
|
AddFile(d);
|
|
}
|
|
foreach (var s in dirInfo.GetDirectories())
|
|
{
|
|
AddFolder(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddFile(FileInfo f)
|
|
{
|
|
AddFileSystemItem(f);
|
|
}
|
|
|
|
public void AddFolder(DirectoryInfo d)
|
|
{
|
|
AddFileSystemItem(d);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the generated hash output of all added objects
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string GenerateHash()
|
|
{
|
|
//flush,close,dispose the writer,then create a new one since it's possible to keep adding after GenerateHash is called.
|
|
|
|
_writer.Flush();
|
|
_writer.Close();
|
|
_writer.Dispose();
|
|
_writer = new StreamWriter(_ms, Encoding.UTF8, 1024, leaveOpen: true);
|
|
|
|
var hashType = CryptoConfig.AllowOnlyFipsAlgorithms ? "SHA1" : "MD5";
|
|
|
|
//create an instance of the correct hashing provider based on the type passed in
|
|
var hasher = HashAlgorithm.Create(hashType);
|
|
if (hasher == null) throw new InvalidOperationException("No hashing type found by name " + hashType);
|
|
using (hasher)
|
|
{
|
|
var buffer = _ms.GetBuffer();
|
|
//get the hashed values created by our selected provider
|
|
var hashedByteArray = hasher.ComputeHash(buffer);
|
|
|
|
//create a StringBuilder object
|
|
var stringBuilder = new StringBuilder();
|
|
|
|
//loop to each byte
|
|
foreach (var b in hashedByteArray)
|
|
{
|
|
//append it to our StringBuilder
|
|
stringBuilder.Append(b.ToString("x2"));
|
|
}
|
|
|
|
//return the hashed value
|
|
return stringBuilder.ToString();
|
|
}
|
|
}
|
|
|
|
protected override void DisposeResources()
|
|
{
|
|
_writer.Close();
|
|
_writer.Dispose();
|
|
_ms.Close();
|
|
_ms.Dispose();
|
|
}
|
|
}
|
|
}
|