Skip to content

Instantly share code, notes, and snippets.

@khurramkhang
Created September 26, 2019 12:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save khurramkhang/8e13cd4c1c9d9a947529036ede377fb6 to your computer and use it in GitHub Desktop.
Save khurramkhang/8e13cd4c1c9d9a947529036ede377fb6 to your computer and use it in GitHub Desktop.
Validate files based on the contents
public class FileType
{
public FileType(string name, string extension, byte?[] signatures, int maximumStartLocation = 0)
{
Name = name;
Extension = extension;
Signatures = signatures;
MaximumStartLocation = maximumStartLocation;
}
public string Name { get; }
public string Extension { get; }
public byte?[] Signatures { get; }
public int MaximumStartLocation { get; }
}
public interface IFileValidationService
{
FileType GetFileType(Stream fileContent);
}
public class FileValidationService : IFileValidationService
{
private readonly ByteWithWildcardComparer _wildcardComparer = new ByteWithWildcardComparer();
private readonly FileType _unknown = new FileType("unknown", string.Empty, new byte?[0]);
private readonly IList<FileType> _fileTypes = new List<FileType>
{
new FileType("Bitmap", "bmp", new byte?[] { 0x42, 0x4d }),
new FileType("Portable Network Graphic", "png", new byte?[] { 0x89, 0x50 }),
new FileType("JPEG", "jpg", new byte?[] { 0xFF, 0xD8}),
new FileType("Graphics Interchange Format", "gif", new byte?[] { 0x47, 0x49 }),
new FileType("Portable Document Format", "pdf", new byte?[] { 0x25, 0x50 }),
new FileType("Rar", "rar", new byte?[] { 0x52, 0x61 }),
new FileType("Zip", "zip", new byte?[] { 0x50, 0x4B }),
new FileType("Word", "doc", new byte?[] { 0xD0, 0xCF }),
new FileType("PPT", "ppt", new byte?[] { 0xD0, 0xCF }),
new FileType("MP3", "mp3", new byte?[] { 0x49, 0x44 })
};
public FileType GetFileType(Stream fileContent)
{
if (fileContent == null)
{
return null;
}
if (!fileContent.CanRead || (fileContent.Position != 0 && !fileContent.CanSeek))
{
return null;
}
if (fileContent.Position != 0)
{
fileContent.Seek(0, SeekOrigin.Begin);
}
var startOfFile = new byte[1024];
fileContent.Read(startOfFile, 0, startOfFile.Length);
var result = _fileTypes.FirstOrDefault(f => StartOfFileContainsFileType(f, startOfFile));
return result ?? _unknown;
}
private bool StartOfFileContainsFileType(FileType fileType, byte[] startOfFile)
{
var counter = 0;
do
{
if (startOfFile.Skip(counter)
.Take(fileType.Signatures.Length)
.Cast<byte?>()
.SequenceEqual(fileType.Signatures, _wildcardComparer))
{
return true;
}
}
while (++counter <= fileType.MaximumStartLocation);
return false;
}
private class ByteWithWildcardComparer : IEqualityComparer<byte?>
{
public bool Equals(byte? x, byte? y)
{
return x == null || y == null || x.GetValueOrDefault() == y.GetValueOrDefault();
}
public int GetHashCode(byte? obj)
{
return obj.GetValueOrDefault().GetHashCode();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment