1 /* 2 KeePass Password Safe - The Open-Source Password Manager 3 Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 using System; 21 using System.Collections.Generic; 22 using System.Diagnostics; 23 using System.Drawing; 24 using System.IO; 25 using System.Text; 26 27 using KeePass.Resources; 28 29 using KeePassLib.Security; 30 using KeePassLib.Utility; 31 32 namespace KeePass.Util 33 { 34 public enum BinaryDataClass 35 { 36 Unknown = 0, 37 Text, 38 RichText, 39 Image, 40 WebDocument 41 } 42 43 public static class BinaryDataClassifier 44 { 45 private static readonly string[] g_vTextExts = new string[] { 46 ".asc", ".bat", ".c", ".cpp", ".css", ".csv", ".h", ".hpp", 47 ".js", ".ps1", ".tab", ".tsv", ".txt" 48 }; 49 50 private static readonly string[] g_vRichTextExts = new string[] { 51 ".rtf" 52 }; 53 54 private static readonly string[] g_vImageExts = new string[] { 55 ".bmp", ".emf", ".exif", ".gif", ".ico", ".jpe", ".jpeg", 56 ".jpg", ".png", ".tif", ".tiff", ".wmf" 57 }; 58 59 private static readonly string[] g_vWebExts = new string[] { 60 ".htm", ".html" 61 62 // The following types can be displayed by Internet Explorer, 63 // but not by the WebBrowser control 64 // ".mht", ".xml", ".xsl", ".xslt" 65 }; 66 UrlHasExt(string strUrl, string[] vExts)67 private static bool UrlHasExt(string strUrl, string[] vExts) 68 { 69 Debug.Assert(strUrl.Trim().ToLowerInvariant() == strUrl); 70 71 foreach(string strExt in vExts) 72 { 73 Debug.Assert(strExt.StartsWith(".")); 74 Debug.Assert(strExt.Trim().ToLower() == strExt); 75 76 if(strUrl.EndsWith(strExt, StringComparison.Ordinal)) 77 return true; 78 } 79 80 return false; 81 } 82 ClassifyUrl(string strUrl)83 public static BinaryDataClass ClassifyUrl(string strUrl) 84 { 85 if(strUrl == null) { Debug.Assert(false); throw new ArgumentNullException("strUrl"); } 86 87 string str = strUrl.Trim().ToLowerInvariant(); 88 89 if(UrlHasExt(str, g_vTextExts)) 90 return BinaryDataClass.Text; 91 if(UrlHasExt(str, g_vRichTextExts)) 92 return BinaryDataClass.RichText; 93 if(UrlHasExt(str, g_vImageExts)) 94 return BinaryDataClass.Image; 95 if(UrlHasExt(str, g_vWebExts)) 96 return BinaryDataClass.WebDocument; 97 98 return BinaryDataClass.Unknown; 99 } 100 ClassifyData(byte[] pbData)101 public static BinaryDataClass ClassifyData(byte[] pbData) 102 { 103 if(pbData == null) { Debug.Assert(false); throw new ArgumentNullException("pbData"); } 104 105 try 106 { 107 Image img = GfxUtil.LoadImage(pbData); 108 if(img != null) 109 { 110 img.Dispose(); 111 return BinaryDataClass.Image; 112 } 113 } 114 catch(Exception) { } 115 116 return BinaryDataClass.Unknown; 117 } 118 119 // Cf. other overload Classify(string strUrl, byte[] pbData)120 public static BinaryDataClass Classify(string strUrl, byte[] pbData) 121 { 122 BinaryDataClass bdc = ClassifyUrl(strUrl); 123 if(bdc != BinaryDataClass.Unknown) return bdc; 124 125 return ClassifyData(pbData); 126 } 127 128 // Cf. other overload Classify(string strUrl, ProtectedBinary pb)129 public static BinaryDataClass Classify(string strUrl, ProtectedBinary pb) 130 { 131 BinaryDataClass bdc = ClassifyUrl(strUrl); 132 if(bdc != BinaryDataClass.Unknown) return bdc; 133 134 if(pb == null) throw new ArgumentNullException("pb"); 135 byte[] pbData = pb.ReadData(); 136 try { bdc = ClassifyData(pbData); } 137 finally { if(pb.IsProtected) MemUtil.ZeroByteArray(pbData); } 138 139 return bdc; 140 } 141 GetStringEncoding(byte[] pbData, out uint uStartOffset)142 public static StrEncodingInfo GetStringEncoding(byte[] pbData, 143 out uint uStartOffset) 144 { 145 if(pbData == null) { Debug.Assert(false); throw new ArgumentNullException("pbData"); } 146 147 uStartOffset = 0; 148 149 List<StrEncodingInfo> lEncs = new List<StrEncodingInfo>(StrUtil.Encodings); 150 lEncs.Sort(BinaryDataClassifier.CompareBySigLengthRev); 151 152 foreach(StrEncodingInfo sei in lEncs) 153 { 154 byte[] pbSig = sei.StartSignature; 155 if((pbSig == null) || (pbSig.Length == 0)) continue; 156 if(pbSig.Length > pbData.Length) continue; 157 158 byte[] pbStart = MemUtil.Mid<byte>(pbData, 0, pbSig.Length); 159 if(MemUtil.ArraysEqual(pbStart, pbSig)) 160 { 161 uStartOffset = (uint)pbSig.Length; 162 return sei; 163 } 164 } 165 166 if((pbData.Length % 4) == 0) 167 { 168 byte[] z3 = new byte[] { 0, 0, 0 }; 169 int i = MemUtil.IndexOf<byte>(pbData, z3); 170 if((i >= 0) && (i < (pbData.Length - 4))) // Ignore last zero char 171 { 172 if((i % 4) == 0) return StrUtil.GetEncoding(StrEncodingType.Utf32BE); 173 if((i % 4) == 1) return StrUtil.GetEncoding(StrEncodingType.Utf32LE); 174 // Don't assume UTF-32 for other offsets 175 } 176 } 177 178 if((pbData.Length % 2) == 0) 179 { 180 int i = Array.IndexOf<byte>(pbData, 0); 181 if((i >= 0) && (i < (pbData.Length - 2))) // Ignore last zero char 182 { 183 if((i % 2) == 0) return StrUtil.GetEncoding(StrEncodingType.Utf16BE); 184 return StrUtil.GetEncoding(StrEncodingType.Utf16LE); 185 } 186 } 187 188 try 189 { 190 UTF8Encoding utf8Throw = new UTF8Encoding(false, true); 191 utf8Throw.GetString(pbData); 192 return StrUtil.GetEncoding(StrEncodingType.Utf8); 193 } 194 catch(Exception) { } 195 196 return StrUtil.GetEncoding(StrEncodingType.Default); 197 } 198 CompareBySigLengthRev(StrEncodingInfo a, StrEncodingInfo b)199 private static int CompareBySigLengthRev(StrEncodingInfo a, StrEncodingInfo b) 200 { 201 Debug.Assert((a != null) && (b != null)); 202 203 int na = 0, nb = 0; 204 if((a != null) && (a.StartSignature != null)) 205 na = a.StartSignature.Length; 206 if((b != null) && (b.StartSignature != null)) 207 nb = b.StartSignature.Length; 208 209 return -(na.CompareTo(nb)); 210 } 211 } 212 } 213