1327 lines
35 KiB
C#
1327 lines
35 KiB
C#
/*
|
|
KeePass Password Safe - The Open-Source Password Manager
|
|
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
|
|
|
|
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Text.RegularExpressions;
|
|
using System.Security.Cryptography;
|
|
|
|
using KeePassLib.Collections;
|
|
using KeePassLib.Native;
|
|
using KeePassLib.Security;
|
|
using KeePassLib.Resources;
|
|
|
|
namespace KeePassLib.Utility
|
|
{
|
|
/// <summary>
|
|
/// Character stream class.
|
|
/// </summary>
|
|
public sealed class CharStream
|
|
{
|
|
private string m_strString = string.Empty;
|
|
private int m_nPos = 0;
|
|
|
|
public CharStream(string str)
|
|
{
|
|
Debug.Assert(str != null);
|
|
if(str == null) throw new ArgumentNullException("str");
|
|
|
|
m_strString = str;
|
|
}
|
|
|
|
public void Seek(SeekOrigin org, int nSeek)
|
|
{
|
|
if(org == SeekOrigin.Begin)
|
|
m_nPos = nSeek;
|
|
else if(org == SeekOrigin.Current)
|
|
m_nPos += nSeek;
|
|
else if(org == SeekOrigin.End)
|
|
m_nPos = m_strString.Length + nSeek;
|
|
}
|
|
|
|
public char ReadChar()
|
|
{
|
|
if(m_nPos < 0) return char.MinValue;
|
|
if(m_nPos >= m_strString.Length) return char.MinValue;
|
|
|
|
char chRet = m_strString[m_nPos];
|
|
++m_nPos;
|
|
return chRet;
|
|
}
|
|
|
|
public char ReadChar(bool bSkipWhiteSpace)
|
|
{
|
|
if(bSkipWhiteSpace == false) return ReadChar();
|
|
|
|
while(true)
|
|
{
|
|
char ch = ReadChar();
|
|
|
|
if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
|
|
return ch;
|
|
}
|
|
}
|
|
|
|
public char PeekChar()
|
|
{
|
|
if(m_nPos < 0) return char.MinValue;
|
|
if(m_nPos >= m_strString.Length) return char.MinValue;
|
|
|
|
return m_strString[m_nPos];
|
|
}
|
|
|
|
public char PeekChar(bool bSkipWhiteSpace)
|
|
{
|
|
if(bSkipWhiteSpace == false) return PeekChar();
|
|
|
|
int iIndex = m_nPos;
|
|
while(true)
|
|
{
|
|
if(iIndex < 0) return char.MinValue;
|
|
if(iIndex >= m_strString.Length) return char.MinValue;
|
|
|
|
char ch = m_strString[iIndex];
|
|
|
|
if((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
|
|
return ch;
|
|
|
|
++iIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum StrEncodingType
|
|
{
|
|
Unknown = 0,
|
|
Default,
|
|
Ascii,
|
|
Utf7,
|
|
Utf8,
|
|
Utf16LE,
|
|
Utf16BE,
|
|
Utf32LE,
|
|
Utf32BE
|
|
}
|
|
|
|
public sealed class StrEncodingInfo
|
|
{
|
|
private readonly StrEncodingType m_type;
|
|
public StrEncodingType Type
|
|
{
|
|
get { return m_type; }
|
|
}
|
|
|
|
private readonly string m_strName;
|
|
public string Name
|
|
{
|
|
get { return m_strName; }
|
|
}
|
|
|
|
private readonly Encoding m_enc;
|
|
public Encoding Encoding
|
|
{
|
|
get { return m_enc; }
|
|
}
|
|
|
|
private readonly uint m_cbCodePoint;
|
|
/// <summary>
|
|
/// Size of a character in bytes.
|
|
/// </summary>
|
|
public uint CodePointSize
|
|
{
|
|
get { return m_cbCodePoint; }
|
|
}
|
|
|
|
private readonly byte[] m_vSig;
|
|
/// <summary>
|
|
/// Start signature of the text (byte order mark).
|
|
/// May be <c>null</c> or empty, if no signature is known.
|
|
/// </summary>
|
|
public byte[] StartSignature
|
|
{
|
|
get { return m_vSig; }
|
|
}
|
|
|
|
public StrEncodingInfo(StrEncodingType t, string strName, Encoding enc,
|
|
uint cbCodePoint, byte[] vStartSig)
|
|
{
|
|
if(strName == null) throw new ArgumentNullException("strName");
|
|
if(enc == null) throw new ArgumentNullException("enc");
|
|
if(cbCodePoint <= 0) throw new ArgumentOutOfRangeException("cbCodePoint");
|
|
|
|
m_type = t;
|
|
m_strName = strName;
|
|
m_enc = enc;
|
|
m_cbCodePoint = cbCodePoint;
|
|
m_vSig = vStartSig;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A class containing various string helper methods.
|
|
/// </summary>
|
|
public static class StrUtil
|
|
{
|
|
public const StringComparison CaseIgnoreCmp = StringComparison.OrdinalIgnoreCase;
|
|
|
|
public static StringComparer CaseIgnoreComparer
|
|
{
|
|
get { return StringComparer.OrdinalIgnoreCase; }
|
|
}
|
|
|
|
private static bool m_bRtl = false;
|
|
public static bool RightToLeft
|
|
{
|
|
get { return m_bRtl; }
|
|
set { m_bRtl = value; }
|
|
}
|
|
|
|
private static UTF8Encoding m_encUtf8 = null;
|
|
public static UTF8Encoding Utf8
|
|
{
|
|
get
|
|
{
|
|
if(m_encUtf8 == null) m_encUtf8 = new UTF8Encoding(false, false);
|
|
return m_encUtf8;
|
|
}
|
|
}
|
|
|
|
private static List<StrEncodingInfo> m_lEncs = null;
|
|
public static IEnumerable<StrEncodingInfo> Encodings
|
|
{
|
|
get
|
|
{
|
|
if(m_lEncs == null)
|
|
{
|
|
m_lEncs = new List<StrEncodingInfo>();
|
|
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Default,
|
|
#if !KeePassLibSD
|
|
Encoding.Default.EncodingName,
|
|
#else
|
|
Encoding.Default.WebName,
|
|
#endif
|
|
Encoding.Default,
|
|
(uint)Encoding.Default.GetBytes("a").Length, null));
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Ascii,
|
|
"ASCII", Encoding.ASCII, 1, null));
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Utf7,
|
|
"Unicode (UTF-7)", Encoding.UTF7, 1, null));
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Utf8,
|
|
"Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Utf16LE,
|
|
"Unicode (UTF-16 LE)", new UnicodeEncoding(false, false),
|
|
2, new byte[] { 0xFF, 0xFE }));
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Utf16BE,
|
|
"Unicode (UTF-16 BE)", new UnicodeEncoding(true, false),
|
|
2, new byte[] { 0xFE, 0xFF }));
|
|
#if !KeePassLibSD
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Utf32LE,
|
|
"Unicode (UTF-32 LE)", new UTF32Encoding(false, false),
|
|
4, new byte[] { 0xFF, 0xFE, 0x0, 0x0 }));
|
|
m_lEncs.Add(new StrEncodingInfo(StrEncodingType.Utf32BE,
|
|
"Unicode (UTF-32 BE)", new UTF32Encoding(true, false),
|
|
4, new byte[] { 0x0, 0x0, 0xFE, 0xFF }));
|
|
#endif
|
|
}
|
|
|
|
return m_lEncs;
|
|
}
|
|
}
|
|
|
|
// public static string RtfPar
|
|
// {
|
|
// // get { return (m_bRtl ? "\\rtlpar " : "\\par "); }
|
|
// get { return "\\par "; }
|
|
// }
|
|
|
|
// /// <summary>
|
|
// /// Convert a string into a valid RTF string.
|
|
// /// </summary>
|
|
// /// <param name="str">Any string.</param>
|
|
// /// <returns>RTF-encoded string.</returns>
|
|
// public static string MakeRtfString(string str)
|
|
// {
|
|
// Debug.Assert(str != null); if(str == null) throw new ArgumentNullException("str");
|
|
// str = str.Replace("\\", "\\\\");
|
|
// str = str.Replace("\r", string.Empty);
|
|
// str = str.Replace("{", "\\{");
|
|
// str = str.Replace("}", "\\}");
|
|
// str = str.Replace("\n", StrUtil.RtfPar);
|
|
// StringBuilder sbEncoded = new StringBuilder();
|
|
// for(int i = 0; i < str.Length; ++i)
|
|
// {
|
|
// char ch = str[i];
|
|
// if((int)ch >= 256)
|
|
// {
|
|
// sbEncoded.Append("\\u");
|
|
// sbEncoded.Append((int)ch);
|
|
// sbEncoded.Append('?');
|
|
// }
|
|
// else sbEncoded.Append(ch);
|
|
// }
|
|
// return sbEncoded.ToString();
|
|
// }
|
|
|
|
/// <summary>
|
|
/// Convert a string into a valid HTML sequence representing that string.
|
|
/// </summary>
|
|
/// <param name="str">String to convert.</param>
|
|
/// <returns>String, HTML-encoded.</returns>
|
|
public static string StringToHtml(string str)
|
|
{
|
|
Debug.Assert(str != null); if(str == null) throw new ArgumentNullException("str");
|
|
|
|
str = str.Replace(@"&", @"&");
|
|
str = str.Replace(@"<", @"<");
|
|
str = str.Replace(@">", @">");
|
|
str = str.Replace("\"", @""");
|
|
str = str.Replace("\'", @"'");
|
|
|
|
str = NormalizeNewLines(str, false);
|
|
str = str.Replace("\n", @"<br />");
|
|
|
|
return str;
|
|
}
|
|
|
|
public static string XmlToString(string str)
|
|
{
|
|
Debug.Assert(str != null); if(str == null) throw new ArgumentNullException("str");
|
|
|
|
str = str.Replace(@"&", @"&");
|
|
str = str.Replace(@"<", @"<");
|
|
str = str.Replace(@">", @">");
|
|
str = str.Replace(@""", "\"");
|
|
str = str.Replace(@"'", "\'");
|
|
|
|
return str;
|
|
}
|
|
|
|
public static string ReplaceCaseInsensitive(string strString, string strFind,
|
|
string strNew)
|
|
{
|
|
Debug.Assert(strString != null); if(strString == null) return strString;
|
|
Debug.Assert(strFind != null); if(strFind == null) return strString;
|
|
Debug.Assert(strNew != null); if(strNew == null) return strString;
|
|
|
|
string str = strString;
|
|
|
|
int nPos = 0;
|
|
while(nPos < str.Length)
|
|
{
|
|
nPos = str.IndexOf(strFind, nPos, StringComparison.OrdinalIgnoreCase);
|
|
if(nPos < 0) break;
|
|
|
|
str = str.Remove(nPos, strFind.Length);
|
|
str = str.Insert(nPos, strNew);
|
|
|
|
nPos += strNew.Length;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Split up a command-line into application and argument.
|
|
/// </summary>
|
|
/// <param name="strCmdLine">Command-line to split.</param>
|
|
/// <param name="strApp">Application path.</param>
|
|
/// <param name="strArgs">Arguments.</param>
|
|
public static void SplitCommandLine(string strCmdLine, out string strApp, out string strArgs)
|
|
{
|
|
Debug.Assert(strCmdLine != null); if(strCmdLine == null) throw new ArgumentNullException("strCmdLine");
|
|
|
|
string str = strCmdLine.Trim();
|
|
|
|
strApp = null; strArgs = null;
|
|
|
|
if(str.StartsWith("\""))
|
|
{
|
|
int nSecond = str.IndexOf('\"', 1);
|
|
if(nSecond >= 1)
|
|
{
|
|
strApp = str.Substring(1, nSecond - 1).Trim();
|
|
strArgs = str.Remove(0, nSecond + 1).Trim();
|
|
}
|
|
}
|
|
|
|
if(strApp == null)
|
|
{
|
|
int nSpace = str.IndexOf(' ');
|
|
|
|
if(nSpace >= 0)
|
|
{
|
|
strApp = str.Substring(0, nSpace);
|
|
strArgs = str.Remove(0, nSpace).Trim();
|
|
}
|
|
else strApp = strCmdLine;
|
|
}
|
|
|
|
if(strApp == null) strApp = string.Empty;
|
|
if(strArgs == null) strArgs = string.Empty;
|
|
}
|
|
|
|
// /// <summary>
|
|
// /// Initialize an RTF document based on given font face and size.
|
|
// /// </summary>
|
|
// /// <param name="sb"><c>StringBuilder</c> to put the generated RTF into.</param>
|
|
// /// <param name="strFontFace">Face name of the font to use.</param>
|
|
// /// <param name="fFontSize">Size of the font to use.</param>
|
|
// public static void InitRtf(StringBuilder sb, string strFontFace, float fFontSize)
|
|
// {
|
|
// Debug.Assert(sb != null); if(sb == null) throw new ArgumentNullException("sb");
|
|
// Debug.Assert(strFontFace != null); if(strFontFace == null) throw new ArgumentNullException("strFontFace");
|
|
// sb.Append("{\\rtf1");
|
|
// if(m_bRtl) sb.Append("\\fbidis");
|
|
// sb.Append("\\ansi\\ansicpg");
|
|
// sb.Append(Encoding.Default.CodePage);
|
|
// sb.Append("\\deff0{\\fonttbl{\\f0\\fswiss MS Sans Serif;}{\\f1\\froman\\fcharset2 Symbol;}{\\f2\\fswiss ");
|
|
// sb.Append(strFontFace);
|
|
// sb.Append(";}{\\f3\\fswiss Arial;}}");
|
|
// sb.Append("{\\colortbl\\red0\\green0\\blue0;}");
|
|
// if(m_bRtl) sb.Append("\\rtldoc");
|
|
// sb.Append("\\deflang1031\\pard\\plain\\f2\\cf0 ");
|
|
// sb.Append("\\fs");
|
|
// sb.Append((int)(fFontSize * 2));
|
|
// if(m_bRtl) sb.Append("\\rtlpar\\qr\\rtlch ");
|
|
// }
|
|
|
|
// /// <summary>
|
|
// /// Convert a simple HTML string to an RTF string.
|
|
// /// </summary>
|
|
// /// <param name="strHtmlString">Input HTML string.</param>
|
|
// /// <returns>RTF string representing the HTML input string.</returns>
|
|
// public static string SimpleHtmlToRtf(string strHtmlString)
|
|
// {
|
|
// StringBuilder sb = new StringBuilder();
|
|
// StrUtil.InitRtf(sb, "Microsoft Sans Serif", 8.25f);
|
|
// sb.Append(" ");
|
|
// string str = MakeRtfString(strHtmlString);
|
|
// str = str.Replace("<b>", "\\b ");
|
|
// str = str.Replace("</b>", "\\b0 ");
|
|
// str = str.Replace("<i>", "\\i ");
|
|
// str = str.Replace("</i>", "\\i0 ");
|
|
// str = str.Replace("<u>", "\\ul ");
|
|
// str = str.Replace("</u>", "\\ul0 ");
|
|
// str = str.Replace("<br />", StrUtil.RtfPar);
|
|
// sb.Append(str);
|
|
// return sb.ToString();
|
|
// }
|
|
|
|
/// <summary>
|
|
/// Convert a <c>Color</c> to a HTML color identifier string.
|
|
/// </summary>
|
|
/// <param name="color">Color to convert.</param>
|
|
/// <param name="bEmptyIfTransparent">If this is <c>true</c>, an empty string
|
|
/// is returned if the color is transparent.</param>
|
|
/// <returns>HTML color identifier string.</returns>
|
|
public static string ColorToUnnamedHtml(Color color, bool bEmptyIfTransparent)
|
|
{
|
|
if(bEmptyIfTransparent && (color.A != 255))
|
|
return string.Empty;
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
byte bt;
|
|
|
|
sb.Append('#');
|
|
|
|
bt = (byte)(color.R >> 4);
|
|
if(bt < 10) sb.Append((char)('0' + bt)); else sb.Append((char)('A' - 10 + bt));
|
|
bt = (byte)(color.R & 0x0F);
|
|
if(bt < 10) sb.Append((char)('0' + bt)); else sb.Append((char)('A' - 10 + bt));
|
|
|
|
bt = (byte)(color.G >> 4);
|
|
if(bt < 10) sb.Append((char)('0' + bt)); else sb.Append((char)('A' - 10 + bt));
|
|
bt = (byte)(color.G & 0x0F);
|
|
if(bt < 10) sb.Append((char)('0' + bt)); else sb.Append((char)('A' - 10 + bt));
|
|
|
|
bt = (byte)(color.B >> 4);
|
|
if(bt < 10) sb.Append((char)('0' + bt)); else sb.Append((char)('A' - 10 + bt));
|
|
bt = (byte)(color.B & 0x0F);
|
|
if(bt < 10) sb.Append((char)('0' + bt)); else sb.Append((char)('A' - 10 + bt));
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Format an exception and convert it to a string.
|
|
/// </summary>
|
|
/// <param name="excp"><c>Exception</c> to convert/format.</param>
|
|
/// <returns>String representing the exception.</returns>
|
|
public static string FormatException(Exception excp)
|
|
{
|
|
string strText = string.Empty;
|
|
|
|
if(excp.Message != null)
|
|
strText += excp.Message + MessageService.NewLine;
|
|
#if !KeePassLibSD
|
|
if(excp.Source != null)
|
|
strText += excp.Source + MessageService.NewLine;
|
|
#endif
|
|
if(excp.StackTrace != null)
|
|
strText += excp.StackTrace + MessageService.NewLine;
|
|
#if !KeePassLibSD
|
|
if(excp.TargetSite != null)
|
|
strText += excp.TargetSite.ToString() + MessageService.NewLine;
|
|
|
|
if(excp.Data != null)
|
|
{
|
|
strText += MessageService.NewLine;
|
|
foreach(DictionaryEntry de in excp.Data)
|
|
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
|
|
MessageService.NewLine;
|
|
}
|
|
#endif
|
|
|
|
if(excp.InnerException != null)
|
|
{
|
|
strText += MessageService.NewLine + "Inner:" + MessageService.NewLine;
|
|
if(excp.InnerException.Message != null)
|
|
strText += excp.InnerException.Message + MessageService.NewLine;
|
|
#if !KeePassLibSD
|
|
if(excp.InnerException.Source != null)
|
|
strText += excp.InnerException.Source + MessageService.NewLine;
|
|
#endif
|
|
if(excp.InnerException.StackTrace != null)
|
|
strText += excp.InnerException.StackTrace + MessageService.NewLine;
|
|
#if !KeePassLibSD
|
|
if(excp.InnerException.TargetSite != null)
|
|
strText += excp.InnerException.TargetSite.ToString();
|
|
|
|
if(excp.InnerException.Data != null)
|
|
{
|
|
strText += MessageService.NewLine;
|
|
foreach(DictionaryEntry de in excp.InnerException.Data)
|
|
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
|
|
MessageService.NewLine;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return strText;
|
|
}
|
|
|
|
public static bool TryParseUShort(string str, out ushort u)
|
|
{
|
|
#if !KeePassLibSD
|
|
return ushort.TryParse(str, out u);
|
|
#else
|
|
try { u = ushort.Parse(str); return true; }
|
|
catch(Exception) { u = 0; return false; }
|
|
#endif
|
|
}
|
|
|
|
public static bool TryParseInt(string str, out int n)
|
|
{
|
|
#if !KeePassLibSD
|
|
return int.TryParse(str, out n);
|
|
#else
|
|
try { n = int.Parse(str); return true; }
|
|
catch(Exception) { n = 0; return false; }
|
|
#endif
|
|
}
|
|
|
|
public static bool TryParseUInt(string str, out uint u)
|
|
{
|
|
#if !KeePassLibSD
|
|
return uint.TryParse(str, out u);
|
|
#else
|
|
try { u = uint.Parse(str); return true; }
|
|
catch(Exception) { u = 0; return false; }
|
|
#endif
|
|
}
|
|
|
|
public static bool TryParseLong(string str, out long n)
|
|
{
|
|
#if !KeePassLibSD
|
|
return long.TryParse(str, out n);
|
|
#else
|
|
try { n = long.Parse(str); return true; }
|
|
catch(Exception) { n = 0; return false; }
|
|
#endif
|
|
}
|
|
|
|
public static bool TryParseULong(string str, out ulong u)
|
|
{
|
|
#if !KeePassLibSD
|
|
return ulong.TryParse(str, out u);
|
|
#else
|
|
try { u = ulong.Parse(str); return true; }
|
|
catch(Exception) { u = 0; return false; }
|
|
#endif
|
|
}
|
|
|
|
public static bool TryParseDateTime(string str, out DateTime dt)
|
|
{
|
|
#if !KeePassLibSD
|
|
return DateTime.TryParse(str, out dt);
|
|
#else
|
|
try { dt = DateTime.Parse(str); return true; }
|
|
catch(Exception) { dt = DateTime.MinValue; return false; }
|
|
#endif
|
|
}
|
|
|
|
public static string CompactString3Dots(string strText, int nMaxChars)
|
|
{
|
|
Debug.Assert(strText != null);
|
|
if(strText == null) throw new ArgumentNullException("strText");
|
|
Debug.Assert(nMaxChars >= 0);
|
|
if(nMaxChars < 0) throw new ArgumentOutOfRangeException("nMaxChars");
|
|
|
|
if(nMaxChars == 0) return string.Empty;
|
|
if(strText.Length <= nMaxChars) return strText;
|
|
|
|
if(nMaxChars <= 3) return strText.Substring(0, nMaxChars);
|
|
|
|
return strText.Substring(0, nMaxChars - 3) + "...";
|
|
}
|
|
|
|
public static string GetStringBetween(string strText, int nStartIndex,
|
|
string strStart, string strEnd)
|
|
{
|
|
int nTemp;
|
|
return GetStringBetween(strText, nStartIndex, strStart, strEnd, out nTemp);
|
|
}
|
|
|
|
public static string GetStringBetween(string strText, int nStartIndex,
|
|
string strStart, string strEnd, out int nInnerStartIndex)
|
|
{
|
|
if(strText == null) throw new ArgumentNullException("strText");
|
|
if(strStart == null) throw new ArgumentNullException("strStart");
|
|
if(strEnd == null) throw new ArgumentNullException("strEnd");
|
|
|
|
nInnerStartIndex = -1;
|
|
|
|
int nIndex = strText.IndexOf(strStart, nStartIndex);
|
|
if(nIndex < 0) return string.Empty;
|
|
|
|
nIndex += strStart.Length;
|
|
|
|
int nEndIndex = strText.IndexOf(strEnd, nIndex);
|
|
if(nEndIndex < 0) return string.Empty;
|
|
|
|
nInnerStartIndex = nIndex;
|
|
return strText.Substring(nIndex, nEndIndex - nIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all characters that are not valid XML characters,
|
|
/// according to http://www.w3.org/TR/xml/#charsets .
|
|
/// </summary>
|
|
/// <param name="strText">Source text.</param>
|
|
/// <returns>Text containing only valid XML characters.</returns>
|
|
public static string SafeXmlString(string strText)
|
|
{
|
|
Debug.Assert(strText != null); // No throw
|
|
if(string.IsNullOrEmpty(strText)) return strText;
|
|
|
|
char[] vChars = strText.ToCharArray();
|
|
StringBuilder sb = new StringBuilder(strText.Length, strText.Length);
|
|
char ch;
|
|
|
|
for(int i = 0; i < vChars.Length; ++i)
|
|
{
|
|
ch = vChars[i];
|
|
|
|
if(((ch >= 0x20) && (ch <= 0xD7FF)) ||
|
|
(ch == 0x9) || (ch == 0xA) || (ch == 0xD) ||
|
|
((ch >= 0xE000) && (ch <= 0xFFFD)))
|
|
sb.Append(ch);
|
|
// Range ((ch >= 0x10000) && (ch <= 0x10FFFF)) excluded
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static Regex m_rxNaturalSplit = null;
|
|
public static int CompareNaturally(string strX, string strY)
|
|
{
|
|
Debug.Assert(strX != null);
|
|
if(strX == null) throw new ArgumentNullException("strX");
|
|
Debug.Assert(strY != null);
|
|
if(strY == null) throw new ArgumentNullException("strY");
|
|
|
|
if(NativeMethods.SupportsStrCmpNaturally)
|
|
return NativeMethods.StrCmpNaturally(strX, strY);
|
|
|
|
strX = strX.ToLower(); // Case-insensitive comparison
|
|
strY = strY.ToLower();
|
|
|
|
if(m_rxNaturalSplit == null)
|
|
m_rxNaturalSplit = new Regex(@"([0-9]+)", RegexOptions.Compiled);
|
|
|
|
string[] vPartsX = m_rxNaturalSplit.Split(strX);
|
|
string[] vPartsY = m_rxNaturalSplit.Split(strY);
|
|
|
|
for(int i = 0; i < Math.Min(vPartsX.Length, vPartsY.Length); ++i)
|
|
{
|
|
string strPartX = vPartsX[i], strPartY = vPartsY[i];
|
|
int iPartCompare;
|
|
|
|
#if KeePassLibSD
|
|
ulong uX = 0, uY = 0;
|
|
try
|
|
{
|
|
uX = ulong.Parse(strPartX);
|
|
uY = ulong.Parse(strPartY);
|
|
iPartCompare = uX.CompareTo(uY);
|
|
}
|
|
catch(Exception) { iPartCompare = strPartX.CompareTo(strPartY); }
|
|
#else
|
|
ulong uX, uY;
|
|
if(ulong.TryParse(strPartX, out uX) && ulong.TryParse(strPartY, out uY))
|
|
iPartCompare = uX.CompareTo(uY);
|
|
else iPartCompare = strPartX.CompareTo(strPartY);
|
|
#endif
|
|
|
|
if(iPartCompare != 0) return iPartCompare;
|
|
}
|
|
|
|
if(vPartsX.Length == vPartsY.Length) return 0;
|
|
if(vPartsX.Length < vPartsY.Length) return -1;
|
|
return 1;
|
|
}
|
|
|
|
public static string RemoveAccelerator(string strMenuText)
|
|
{
|
|
if(strMenuText == null) throw new ArgumentNullException("strMenuText");
|
|
|
|
string str = strMenuText;
|
|
|
|
for(char ch = 'A'; ch <= 'Z'; ++ch)
|
|
{
|
|
string strEnhAcc = @"(&" + ch.ToString() + @")";
|
|
if(str.IndexOf(strEnhAcc) >= 0)
|
|
{
|
|
str = str.Replace(@" " + strEnhAcc, string.Empty);
|
|
str = str.Replace(strEnhAcc, string.Empty);
|
|
}
|
|
}
|
|
|
|
str = str.Replace(@"&", string.Empty);
|
|
|
|
return str;
|
|
}
|
|
|
|
public static bool IsHexString(string str, bool bStrict)
|
|
{
|
|
if(str == null) throw new ArgumentNullException("str");
|
|
if(str.Length == 0) return true;
|
|
|
|
foreach(char ch in str)
|
|
{
|
|
if((ch >= '0') && (ch <= '9')) continue;
|
|
if((ch >= 'a') && (ch <= 'z')) continue;
|
|
if((ch >= 'A') && (ch <= 'Z')) continue;
|
|
|
|
if(bStrict) return false;
|
|
|
|
if((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
|
|
continue;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if !KeePassLibSD
|
|
private static readonly char[] m_vPatternPartsSep = new char[]{ '*' };
|
|
public static bool SimplePatternMatch(string strPattern, string strText,
|
|
StringComparison sc)
|
|
{
|
|
if(strPattern == null) throw new ArgumentNullException("strPattern");
|
|
if(strText == null) throw new ArgumentNullException("strText");
|
|
|
|
if(strPattern.IndexOf('*') < 0) return strText.Equals(strPattern, sc);
|
|
|
|
string[] vPatternParts = strPattern.Split(m_vPatternPartsSep,
|
|
StringSplitOptions.RemoveEmptyEntries);
|
|
if(vPatternParts == null) { Debug.Assert(false); return true; }
|
|
if(vPatternParts.Length == 0) return true;
|
|
|
|
if(strText.Length == 0) return false;
|
|
|
|
if(!strPattern.StartsWith(@"*") && !strText.StartsWith(vPatternParts[0], sc))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!strPattern.EndsWith(@"*") && !strText.EndsWith(vPatternParts[
|
|
vPatternParts.Length - 1], sc))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int iOffset = 0;
|
|
for(int i = 0; i < vPatternParts.Length; ++i)
|
|
{
|
|
string strPart = vPatternParts[i];
|
|
|
|
int iFound = strText.IndexOf(strPart, iOffset, sc);
|
|
if(iFound < iOffset) return false;
|
|
|
|
iOffset = iFound + strPart.Length;
|
|
if(iOffset == strText.Length) return (i == (vPatternParts.Length - 1));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif // !KeePassLibSD
|
|
|
|
public static bool StringToBool(string str)
|
|
{
|
|
if(string.IsNullOrEmpty(str)) return false; // No assert
|
|
|
|
string s = str.Trim().ToLower();
|
|
if(s == "true") return true;
|
|
if(s == "yes") return true;
|
|
if(s == "1") return true;
|
|
if(s == "enabled") return true;
|
|
if(s == "checked") return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool? StringToBoolEx(string str)
|
|
{
|
|
if(string.IsNullOrEmpty(str)) return null;
|
|
|
|
string s = str.Trim().ToLower();
|
|
if(s == "true") return true;
|
|
if(s == "false") return false;
|
|
|
|
return null;
|
|
}
|
|
|
|
public static string BoolToString(bool bValue)
|
|
{
|
|
return (bValue ? "true" : "false");
|
|
}
|
|
|
|
public static string BoolToStringEx(bool? bValue)
|
|
{
|
|
if(bValue.HasValue) return BoolToString(bValue.Value);
|
|
return "null";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Normalize new line characters in a string. Input strings may
|
|
/// contain mixed new line character sequences from all commonly
|
|
/// used operating systems (i.e. \r\n from Windows, \n from Unix
|
|
/// and \r from Mac OS.
|
|
/// </summary>
|
|
/// <param name="str">String with mixed new line characters.</param>
|
|
/// <param name="bWindows">If <c>true</c>, new line characters
|
|
/// are normalized for Windows (\r\n); if <c>false</c>, new line
|
|
/// characters are normalized for Unix (\n).</param>
|
|
/// <returns>String with normalized new line characters.</returns>
|
|
public static string NormalizeNewLines(string str, bool bWindows)
|
|
{
|
|
if(string.IsNullOrEmpty(str)) return str;
|
|
|
|
str = str.Replace("\r\n", "\n");
|
|
str = str.Replace("\r", "\n");
|
|
|
|
if(bWindows) str = str.Replace("\n", "\r\n");
|
|
|
|
return str;
|
|
}
|
|
|
|
private static char[] m_vNewLineChars = null;
|
|
public static void NormalizeNewLines(ProtectedStringDictionary dict,
|
|
bool bWindows)
|
|
{
|
|
if(dict == null) { Debug.Assert(false); return; }
|
|
|
|
if(m_vNewLineChars == null)
|
|
m_vNewLineChars = new char[]{ '\r', '\n' };
|
|
|
|
List<string> vKeys = dict.GetKeys();
|
|
foreach(string strKey in vKeys)
|
|
{
|
|
ProtectedString ps = dict.Get(strKey);
|
|
if(ps == null) { Debug.Assert(false); continue; }
|
|
|
|
string strValue = ps.ReadString();
|
|
if(strValue.IndexOfAny(m_vNewLineChars) < 0) continue;
|
|
|
|
dict.Set(strKey, new ProtectedString(ps.IsProtected,
|
|
NormalizeNewLines(strValue, bWindows)));
|
|
}
|
|
}
|
|
|
|
public static string AlphaNumericOnly(string str)
|
|
{
|
|
if(string.IsNullOrEmpty(str)) return str;
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
for(int i = 0; i < str.Length; ++i)
|
|
{
|
|
char ch = str[i];
|
|
if(((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
|
|
((ch >= '0') && (ch <= '9')))
|
|
sb.Append(ch);
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static string FormatDataSize(ulong uBytes)
|
|
{
|
|
const ulong uKB = 1024;
|
|
const ulong uMB = uKB * uKB;
|
|
const ulong uGB = uMB * uKB;
|
|
const ulong uTB = uGB * uKB;
|
|
|
|
if(uBytes == 0) return "0 KB";
|
|
if(uBytes <= uKB) return "1 KB";
|
|
if(uBytes <= uMB) return (((uBytes - 1UL) / uKB) + 1UL).ToString() + " KB";
|
|
if(uBytes <= uGB) return (((uBytes - 1UL) / uMB) + 1UL).ToString() + " MB";
|
|
if(uBytes <= uTB) return (((uBytes - 1UL) / uGB) + 1UL).ToString() + " GB";
|
|
|
|
return (((uBytes - 1UL)/ uTB) + 1UL).ToString() + " TB";
|
|
}
|
|
|
|
public static string FormatDataSizeKB(ulong uBytes)
|
|
{
|
|
const ulong uKB = 1024;
|
|
|
|
if(uBytes == 0) return "0 KB";
|
|
if(uBytes <= uKB) return "1 KB";
|
|
|
|
return (((uBytes - 1UL) / uKB) + 1UL).ToString() + " KB";
|
|
}
|
|
|
|
private static readonly char[] m_vVersionSep = new char[]{ '.', ',' };
|
|
public static ulong ParseVersion(string strVersion)
|
|
{
|
|
if(strVersion == null) { Debug.Assert(false); return 0; }
|
|
|
|
string[] vVer = strVersion.Split(m_vVersionSep);
|
|
if((vVer == null) || (vVer.Length == 0)) { Debug.Assert(false); return 0; }
|
|
|
|
ushort uPart;
|
|
StrUtil.TryParseUShort(vVer[0].Trim(), out uPart);
|
|
ulong uVer = ((ulong)uPart << 48);
|
|
|
|
if(vVer.Length >= 2)
|
|
{
|
|
StrUtil.TryParseUShort(vVer[1].Trim(), out uPart);
|
|
uVer |= ((ulong)uPart << 32);
|
|
}
|
|
|
|
if(vVer.Length >= 3)
|
|
{
|
|
StrUtil.TryParseUShort(vVer[2].Trim(), out uPart);
|
|
uVer |= ((ulong)uPart << 16);
|
|
}
|
|
|
|
if(vVer.Length >= 4)
|
|
{
|
|
StrUtil.TryParseUShort(vVer[3].Trim(), out uPart);
|
|
uVer |= (ulong)uPart;
|
|
}
|
|
|
|
return uVer;
|
|
}
|
|
|
|
public static string VersionToString(ulong uVersion)
|
|
{
|
|
return VersionToString(uVersion, false);
|
|
}
|
|
|
|
public static string VersionToString(ulong uVersion,
|
|
bool bEnsureAtLeastTwoComp)
|
|
{
|
|
string str = string.Empty;
|
|
bool bMultiComp = false;
|
|
|
|
for(int i = 0; i < 4; ++i)
|
|
{
|
|
ushort us = (ushort)(uVersion & 0xFFFFUL);
|
|
|
|
if((us != 0) || (str.Length > 0))
|
|
{
|
|
if(str.Length > 0)
|
|
{
|
|
str = "." + str;
|
|
bMultiComp = true;
|
|
}
|
|
|
|
str = us.ToString() + str;
|
|
}
|
|
|
|
uVersion >>= 16;
|
|
}
|
|
|
|
if(bEnsureAtLeastTwoComp && !bMultiComp && (str.Length > 0))
|
|
str += ".0";
|
|
|
|
return str;
|
|
}
|
|
|
|
private static readonly byte[] m_pbOptEnt = { 0xA5, 0x74, 0x2E, 0xEC };
|
|
|
|
public static string EncryptString(string strPlainText)
|
|
{
|
|
if(string.IsNullOrEmpty(strPlainText)) return string.Empty;
|
|
|
|
try
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
catch(Exception) { Debug.Assert(false); }
|
|
|
|
return strPlainText;
|
|
}
|
|
|
|
public static string DecryptString(string strCipherText)
|
|
{
|
|
if(string.IsNullOrEmpty(strCipherText)) return string.Empty;
|
|
|
|
try
|
|
{
|
|
byte[] pbEnc = Convert.FromBase64String(strCipherText);
|
|
throw new NotSupportedException();
|
|
|
|
}
|
|
catch(Exception) { Debug.Assert(false); }
|
|
|
|
return strCipherText;
|
|
}
|
|
|
|
public static string SerializeIntArray(int[] vNumbers)
|
|
{
|
|
if(vNumbers == null) throw new ArgumentNullException("vNumbers");
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
for(int i = 0; i < vNumbers.Length; ++i)
|
|
{
|
|
if(i > 0) sb.Append(' ');
|
|
sb.Append(vNumbers[i]);
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static int[] DeserializeIntArray(string strSerialized)
|
|
{
|
|
if(strSerialized == null) throw new ArgumentNullException("strSerialized");
|
|
if(strSerialized.Length == 0) return new int[0];
|
|
|
|
string[] vParts = strSerialized.Split(' ');
|
|
int[] v = new int[vParts.Length];
|
|
|
|
for(int i = 0; i < vParts.Length; ++i)
|
|
{
|
|
int n;
|
|
if(!TryParseInt(vParts[i], out n)) { Debug.Assert(false); }
|
|
v[i] = n;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
private static readonly char[] m_vTagSep = new char[]{ ',', ';', ':' };
|
|
public static string TagsToString(List<string> vTags, bool bForDisplay)
|
|
{
|
|
if(vTags == null) throw new ArgumentNullException("vTags");
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
bool bFirst = true;
|
|
|
|
foreach(string strTag in vTags)
|
|
{
|
|
if(string.IsNullOrEmpty(strTag)) { Debug.Assert(false); continue; }
|
|
Debug.Assert(strTag.IndexOfAny(m_vTagSep) < 0);
|
|
|
|
if(!bFirst)
|
|
{
|
|
if(bForDisplay) sb.Append(", ");
|
|
else sb.Append(';');
|
|
}
|
|
sb.Append(strTag);
|
|
|
|
bFirst = false;
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static List<string> StringToTags(string strTags)
|
|
{
|
|
if(strTags == null) throw new ArgumentNullException("strTags");
|
|
|
|
List<string> lTags = new List<string>();
|
|
if(strTags.Length == 0) return lTags;
|
|
|
|
string[] vTags = strTags.Split(m_vTagSep);
|
|
foreach(string strTag in vTags)
|
|
{
|
|
string strFlt = strTag.Trim();
|
|
if(strFlt.Length > 0) lTags.Add(strFlt);
|
|
}
|
|
|
|
return lTags;
|
|
}
|
|
|
|
public static string Obfuscate(string strPlain)
|
|
{
|
|
if(strPlain == null) { Debug.Assert(false); return string.Empty; }
|
|
if(strPlain.Length == 0) return string.Empty;
|
|
|
|
byte[] pb = StrUtil.Utf8.GetBytes(strPlain);
|
|
|
|
Array.Reverse(pb);
|
|
for(int i = 0; i < pb.Length; ++i) pb[i] = (byte)(pb[i] ^ 0x65);
|
|
|
|
#if !KeePassLibSD
|
|
return Convert.ToBase64String(pb, Base64FormattingOptions.None);
|
|
#else
|
|
return Convert.ToBase64String(pb);
|
|
#endif
|
|
}
|
|
|
|
public static string Deobfuscate(string strObf)
|
|
{
|
|
if(strObf == null) { Debug.Assert(false); return string.Empty; }
|
|
if(strObf.Length == 0) return string.Empty;
|
|
|
|
try
|
|
{
|
|
byte[] pb = Convert.FromBase64String(strObf);
|
|
|
|
for(int i = 0; i < pb.Length; ++i) pb[i] = (byte)(pb[i] ^ 0x65);
|
|
Array.Reverse(pb);
|
|
|
|
return StrUtil.Utf8.GetString(pb, 0, pb.Length);
|
|
}
|
|
catch(Exception) { Debug.Assert(false); }
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Split a string and include the separators in the splitted array.
|
|
/// </summary>
|
|
/// <param name="str">String to split.</param>
|
|
/// <param name="vSeps">Separators.</param>
|
|
/// <param name="bCaseSensitive">Specifies whether separators are
|
|
/// matched case-sensitively or not.</param>
|
|
/// <returns>Splitted string including separators.</returns>
|
|
public static List<string> SplitWithSep(string str, string[] vSeps,
|
|
bool bCaseSensitive)
|
|
{
|
|
if(str == null) throw new ArgumentNullException("str");
|
|
if(vSeps == null) throw new ArgumentNullException("vSeps");
|
|
|
|
List<string> v = new List<string>();
|
|
while(true)
|
|
{
|
|
int minIndex = int.MaxValue, minSep = -1;
|
|
for(int i = 0; i < vSeps.Length; ++i)
|
|
{
|
|
string strSep = vSeps[i];
|
|
if(string.IsNullOrEmpty(strSep)) { Debug.Assert(false); continue; }
|
|
|
|
int iIndex = (bCaseSensitive ? str.IndexOf(strSep) :
|
|
str.IndexOf(strSep, StrUtil.CaseIgnoreCmp));
|
|
if((iIndex >= 0) && (iIndex < minIndex))
|
|
{
|
|
minIndex = iIndex;
|
|
minSep = i;
|
|
}
|
|
}
|
|
|
|
if(minIndex == int.MaxValue) break;
|
|
|
|
v.Add(str.Substring(0, minIndex));
|
|
v.Add(vSeps[minSep]);
|
|
|
|
str = str.Substring(minIndex + vSeps[minSep].Length);
|
|
}
|
|
|
|
v.Add(str);
|
|
return v;
|
|
}
|
|
|
|
public static string MultiToSingleLine(string strMulti)
|
|
{
|
|
if(strMulti == null) { Debug.Assert(false); return string.Empty; }
|
|
if(strMulti.Length == 0) return string.Empty;
|
|
|
|
string str = strMulti;
|
|
str = str.Replace("\r\n", " ");
|
|
str = str.Replace("\r", " ");
|
|
str = str.Replace("\n", " ");
|
|
|
|
return str;
|
|
}
|
|
|
|
public static List<string> SplitSearchTerms(string strSearch)
|
|
{
|
|
List<string> l = new List<string>();
|
|
if(strSearch == null) { Debug.Assert(false); return l; }
|
|
|
|
StringBuilder sbTerm = new StringBuilder();
|
|
bool bQuoted = false;
|
|
|
|
for(int i = 0; i < strSearch.Length; ++i)
|
|
{
|
|
char ch = strSearch[i];
|
|
|
|
if(((ch == ' ') || (ch == '\t') || (ch == '\r') ||
|
|
(ch == '\n')) && !bQuoted)
|
|
{
|
|
if(sbTerm.Length > 0) l.Add(sbTerm.ToString());
|
|
|
|
sbTerm.Remove(0, sbTerm.Length);
|
|
}
|
|
else if(ch == '\"') bQuoted = !bQuoted;
|
|
else sbTerm.Append(ch);
|
|
}
|
|
if(sbTerm.Length > 0) l.Add(sbTerm.ToString());
|
|
|
|
return l;
|
|
}
|
|
|
|
public static int CompareLengthGt(string x, string y)
|
|
{
|
|
if(x.Length == y.Length) return 0;
|
|
return ((x.Length > y.Length) ? -1 : 1);
|
|
}
|
|
|
|
public static bool IsDataUri(string strUri)
|
|
{
|
|
if(strUri == null) { Debug.Assert(false); return false; }
|
|
|
|
return strUri.StartsWith("data:", StrUtil.CaseIgnoreCmp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a data URI (according to RFC 2397).
|
|
/// </summary>
|
|
/// <param name="pbData">Data to encode.</param>
|
|
/// <param name="strMimeType">Optional MIME type. If <c>null</c>,
|
|
/// an appropriate type is used.</param>
|
|
/// <returns>Data URI.</returns>
|
|
public static string DataToDataUri(byte[] pbData, string strMimeType)
|
|
{
|
|
if(pbData == null) throw new ArgumentNullException("pbData");
|
|
|
|
if(strMimeType == null) strMimeType = "application/octet-stream";
|
|
|
|
#if !KeePassLibSD
|
|
return ("data:" + strMimeType + ";base64," + Convert.ToBase64String(
|
|
pbData, Base64FormattingOptions.None));
|
|
#else
|
|
return ("data:" + strMimeType + ";base64," + Convert.ToBase64String(
|
|
pbData));
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert a data URI (according to RFC 2397) to binary data.
|
|
/// </summary>
|
|
/// <param name="strDataUri">Data URI to decode.</param>
|
|
/// <returns>Decoded binary data.</returns>
|
|
public static byte[] DataUriToData(string strDataUri)
|
|
{
|
|
if(strDataUri == null) throw new ArgumentNullException("strDataUri");
|
|
if(!strDataUri.StartsWith("data:", StrUtil.CaseIgnoreCmp)) return null;
|
|
|
|
int iSep = strDataUri.IndexOf(',');
|
|
if(iSep < 0) return null;
|
|
|
|
string strDesc = strDataUri.Substring(5, iSep - 5);
|
|
bool bBase64 = strDesc.EndsWith(";base64", StrUtil.CaseIgnoreCmp);
|
|
|
|
string strData = strDataUri.Substring(iSep + 1);
|
|
|
|
if(bBase64) return Convert.FromBase64String(strData);
|
|
|
|
MemoryStream ms = new MemoryStream();
|
|
|
|
string[] v = strData.Split('%');
|
|
byte[] pb = Encoding.ASCII.GetBytes(v[0]);
|
|
ms.Write(pb, 0, pb.Length);
|
|
for(int i = 1; i < v.Length; ++i)
|
|
{
|
|
ms.WriteByte(Convert.ToByte(v[i].Substring(0, 2), 16));
|
|
pb = Encoding.ASCII.GetBytes(v[i].Substring(2));
|
|
ms.Write(pb, 0, pb.Length);
|
|
}
|
|
|
|
pb = ms.ToArray();
|
|
ms.Close();
|
|
return pb;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove placeholders from a string (wrapped in '{' and '}').
|
|
/// This doesn't remove environment variables (wrapped in '%').
|
|
/// </summary>
|
|
public static string RemovePlaceholders(string str)
|
|
{
|
|
if(str == null) { Debug.Assert(false); return string.Empty; }
|
|
|
|
while(true)
|
|
{
|
|
int iPlhStart = str.IndexOf('{');
|
|
if(iPlhStart < 0) break;
|
|
|
|
int iPlhEnd = str.IndexOf('}', iPlhStart); // '{' might be at end
|
|
if(iPlhEnd < 0) break;
|
|
|
|
str = (str.Substring(0, iPlhStart) + str.Substring(iPlhEnd + 1));
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
public static StrEncodingInfo GetEncoding(StrEncodingType t)
|
|
{
|
|
foreach(StrEncodingInfo sei in StrUtil.Encodings)
|
|
{
|
|
if(sei.Type == t) return sei;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static StrEncodingInfo GetEncoding(string strName)
|
|
{
|
|
foreach(StrEncodingInfo sei in StrUtil.Encodings)
|
|
{
|
|
if(sei.Name == strName) return sei;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|