diff --git a/ModernKeePassLib/Cryptography/CryptoRandom.cs b/ModernKeePassLib/Cryptography/CryptoRandom.cs index dc46722..acf96bf 100644 --- a/ModernKeePassLib/Cryptography/CryptoRandom.cs +++ b/ModernKeePassLib/Cryptography/CryptoRandom.cs @@ -234,7 +234,7 @@ namespace ModernKeePassLib.Cryptography try { #if KeePassUAP - f(DiagnosticsExt.GetProcessEntropy(), true); + f(DiagnosticsExt.GetProcessEntropy(), true); #elif !KeePassLibSD && !ModernKeePassLib using(Process p = Process.GetCurrentProcess()) { @@ -282,7 +282,6 @@ namespace ModernKeePassLib.Cryptography { byte[] pb = new byte[32]; - try { m_rng.GetBytes(pb); } catch(Exception) { diff --git a/ModernKeePassLib/Cryptography/CryptoUtil.cs b/ModernKeePassLib/Cryptography/CryptoUtil.cs index e875054..ff7044f 100644 --- a/ModernKeePassLib/Cryptography/CryptoUtil.cs +++ b/ModernKeePassLib/Cryptography/CryptoUtil.cs @@ -108,7 +108,7 @@ namespace ModernKeePassLib.Cryptography } #if !ModernKeePassLib - internal static byte[] HashSha256(string strFilePath) + internal static byte[] HashSha256(string strFilePath) { byte[] pbHash = null; diff --git a/ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs b/ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs index a3a6106..929cf34 100644 --- a/ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs +++ b/ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs @@ -151,6 +151,8 @@ namespace ModernKeePassLib.Cryptography.KeyDerivation aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0); aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16); } + + aes.Reset(); #else byte[] pbIV = new byte[16]; diff --git a/ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs b/ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs index 35c5521..e7c75e0 100644 --- a/ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs +++ b/ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs @@ -468,7 +468,6 @@ namespace ModernKeePassLib.Cryptography.KeyDerivation #if ModernKeePassLib Task.Factory.StartNew(FillSegmentThr, ti); - //ThreadPool.RunAsync(a => FillSegmentThr(ti)); #else if(!ThreadPool.QueueUserWorkItem(FillSegmentThr, ti)) { diff --git a/ModernKeePassLib/Keys/CompositeKey.cs b/ModernKeePassLib/Keys/CompositeKey.cs index 8663cf4..9d8d6a4 100644 --- a/ModernKeePassLib/Keys/CompositeKey.cs +++ b/ModernKeePassLib/Keys/CompositeKey.cs @@ -281,8 +281,9 @@ namespace ModernKeePassLib.Keys #if ModernKeePassLib return GenerateKey32(p); #else - if (sl == null) return GenerateKey32(p); - CkGkTaskInfo ti = new CkGkTaskInfo(); + if(sl == null) return GenerateKey32(p); + + CkGkTaskInfo ti = new CkGkTaskInfo(); ThreadStart f = delegate() { diff --git a/ModernKeePassLib/Keys/KcpKeyFile.cs b/ModernKeePassLib/Keys/KcpKeyFile.cs index e50e687..97844e1 100644 --- a/ModernKeePassLib/Keys/KcpKeyFile.cs +++ b/ModernKeePassLib/Keys/KcpKeyFile.cs @@ -70,7 +70,7 @@ namespace ModernKeePassLib.Keys Construct(IOConnectionInfo.FromByteArray(keyFile), false); } #else - public KcpKeyFile(string strKeyFile) + public KcpKeyFile(string strKeyFile) { Construct(IOConnectionInfo.FromPath(strKeyFile), false); } @@ -191,7 +191,7 @@ namespace ModernKeePassLib.Keys #if ModernKeePassLib public static byte[] Create(byte[] pbAdditionalEntropy) #else - public static void Create(string strFilePath, byte[] pbAdditionalEntropy) + public static void Create(string strFilePath, byte[] pbAdditionalEntropy) #endif { byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32); @@ -214,7 +214,7 @@ namespace ModernKeePassLib.Keys #if ModernKeePassLib return CreateXmlKeyFile(pbFinalKey32); #else - CreateXmlKeyFile(strFilePath, pbFinalKey32); + CreateXmlKeyFile(strFilePath, pbFinalKey32); #endif } diff --git a/ModernKeePassLib/ModernKeePassLib.csproj b/ModernKeePassLib/ModernKeePassLib.csproj index d97a961..32d1253 100644 --- a/ModernKeePassLib/ModernKeePassLib.csproj +++ b/ModernKeePassLib/ModernKeePassLib.csproj @@ -3,11 +3,11 @@ netstandard1.2 Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases. - 2.44.3 + 2.45.1 Geoffroy Bonneville wismna https://github.com/wismna/ModernKeePass - Implementation of KeePass library version 2.44 + Implementation of KeePass library version 2.45 Copyright © 2018 Geoffroy Bonneville KeePass KeePassLib Portable PCL NetStandard diff --git a/ModernKeePassLib/Native/ClipboardU.cs b/ModernKeePassLib/Native/ClipboardU.cs index 87760b4..a39f837 100644 --- a/ModernKeePassLib/Native/ClipboardU.cs +++ b/ModernKeePassLib/Native/ClipboardU.cs @@ -1,6 +1,6 @@ /* KeePass Password Safe - The Open-Source Password Manager - Copyright (C) 2003-2018 Dominik Reichl + Copyright (C) 2003-2020 Dominik Reichl 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 @@ -27,7 +27,7 @@ namespace ModernKeePassLib.Native { internal static class ClipboardU { - private const string XSel = "xsel"; + internal const string XSel = "xsel"; private const string XSelV = "--version"; private const string XSelR = "--output --clipboard"; private const string XSelC = "--clear --clipboard"; diff --git a/ModernKeePassLib/Native/NativeLib.cs b/ModernKeePassLib/Native/NativeLib.cs index 92fc17e..dabbc3a 100644 --- a/ModernKeePassLib/Native/NativeLib.cs +++ b/ModernKeePassLib/Native/NativeLib.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; @@ -273,7 +274,13 @@ namespace ModernKeePassLib.Native return strOutput; } #if DEBUG - catch(Exception ex) { Debug.Assert(ex is ThreadAbortException); } + catch(ThreadAbortException) { } + catch(Win32Exception exW) + { + Debug.Assert((strAppPath == ClipboardU.XSel) && + (exW.NativeErrorCode == 2)); // XSel not found + } + catch(Exception) { Debug.Assert(false); } #else catch(Exception) { } #endif diff --git a/ModernKeePassLib/PwDefs.cs b/ModernKeePassLib/PwDefs.cs index 66174cf..4332a2a 100644 --- a/ModernKeePassLib/PwDefs.cs +++ b/ModernKeePassLib/PwDefs.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Net; using System.Xml.Serialization; using ModernKeePassLib.Delegates; @@ -55,18 +56,18 @@ namespace ModernKeePassLib /// e.g. 2.19 = 0x02130000. /// It is highly recommended to use FileVersion64 instead. /// - public static readonly uint Version32 = 0x022C0000; + public static readonly uint Version32 = 0x022D0000; /// /// Version, encoded as 64-bit unsigned integer /// (component-wise, 16 bits per component). /// - public static readonly ulong FileVersion64 = 0x0002002C00000000UL; + public static readonly ulong FileVersion64 = 0x0002002D00000000UL; /// /// Version, encoded as string. /// - public static readonly string VersionString = "2.44"; + public static readonly string VersionString = "2.45"; public static readonly string Copyright = @"Copyright © 2003-2020 Dominik Reichl"; @@ -187,6 +188,8 @@ namespace ModernKeePassLib /// internal const int UIUpdateDelay = 50; + internal const uint QualityBitsWeak = 79; + /// /// Check if a name is a standard field name. /// @@ -507,13 +510,13 @@ namespace ModernKeePassLib public sealed class ObjectTouchedEventArgs : EventArgs { - private object m_o; + private readonly object m_o; public object Object { get { return m_o; } } - private bool m_bModified; + private readonly bool m_bModified; public bool Modified { get { return m_bModified; } } - private bool m_bParentsTouched; + private readonly bool m_bParentsTouched; public bool ParentsTouched { get { return m_bParentsTouched; } } public ObjectTouchedEventArgs(object o, bool bModified, @@ -527,13 +530,13 @@ namespace ModernKeePassLib public sealed class IOAccessEventArgs : EventArgs { - private IOConnectionInfo m_ioc; + private readonly IOConnectionInfo m_ioc; public IOConnectionInfo IOConnectionInfo { get { return m_ioc; } } - private IOConnectionInfo m_ioc2; + private readonly IOConnectionInfo m_ioc2; public IOConnectionInfo IOConnectionInfo2 { get { return m_ioc2; } } - private IOAccessType m_t; + private readonly IOAccessType m_t; public IOAccessType Type { get { return m_t; } } public IOAccessEventArgs(IOConnectionInfo ioc, IOConnectionInfo ioc2, @@ -544,4 +547,21 @@ namespace ModernKeePassLib m_t = t; } } + + public sealed class IOWebRequestEventArgs : EventArgs + { +#if !ModernKeePassLib + private readonly WebRequest m_wr; + public WebRequest Request { get { return m_wr; } } + + private readonly IOConnectionInfo m_ioc; + public IOConnectionInfo IOConnectionInfo { get { return m_ioc; } } + + public IOWebRequestEventArgs(WebRequest r, IOConnectionInfo ioc) + { + m_wr = r; + m_ioc = ioc; + } +#endif + } } diff --git a/ModernKeePassLib/PwEnums.cs b/ModernKeePassLib/PwEnums.cs index 5e95704..0bf6a75 100644 --- a/ModernKeePassLib/PwEnums.cs +++ b/ModernKeePassLib/PwEnums.cs @@ -235,28 +235,28 @@ namespace ModernKeePassLib None = 0, /// - /// The IO connection is being opened for reading. + /// The I/O connection is being opened for reading. /// Read = 1, /// - /// The IO connection is being opened for writing. + /// The I/O connection is being opened for writing. /// Write = 2, /// - /// The IO connection is being opened for testing + /// The I/O connection is being opened for testing /// whether a file/object exists. /// Exists = 3, /// - /// The IO connection is being opened for deleting a file/object. + /// The I/O connection is being opened for deleting a file/object. /// Delete = 4, /// - /// The IO connection is being opened for renaming/moving a file/object. + /// The I/O connection is being opened for renaming/moving a file/object. /// Move = 5 } diff --git a/ModernKeePassLib/PwGroup.cs b/ModernKeePassLib/PwGroup.cs index 2779c93..021832f 100644 --- a/ModernKeePassLib/PwGroup.cs +++ b/ModernKeePassLib/PwGroup.cs @@ -1725,7 +1725,7 @@ namespace ModernKeePassLib { Dictionary d = new Dictionary(); - GAction fAdd = delegate(string str) + Action fAdd = delegate(string str) { if(!string.IsNullOrEmpty(str)) d[str] = true; }; diff --git a/ModernKeePassLib/Resources/KLRes.Generated.cs b/ModernKeePassLib/Resources/KLRes.Generated.cs index c87b503..43c61ce 100644 --- a/ModernKeePassLib/Resources/KLRes.Generated.cs +++ b/ModernKeePassLib/Resources/KLRes.Generated.cs @@ -9,7 +9,7 @@ namespace ModernKeePassLib.Resources /// /// A strongly-typed resource class, for looking up localized strings, etc. /// - public static class KLRes + public static partial class KLRes { private static string TryGetEx(Dictionary dictNew, string strName, string strDefault) diff --git a/ModernKeePassLib/Serialization/FileTransactionEx.cs b/ModernKeePassLib/Serialization/FileTransactionEx.cs index 85ffb25..fa31166 100644 --- a/ModernKeePassLib/Serialization/FileTransactionEx.cs +++ b/ModernKeePassLib/Serialization/FileTransactionEx.cs @@ -524,7 +524,7 @@ namespace ModernKeePassLib.Serialization return false; } #if !ModernKeePassLib - internal static void ClearOld() + internal static void ClearOld() { try { diff --git a/ModernKeePassLib/Serialization/IOConnection.cs b/ModernKeePassLib/Serialization/IOConnection.cs index 9ccfc55..172c58f 100644 --- a/ModernKeePassLib/Serialization/IOConnection.cs +++ b/ModernKeePassLib/Serialization/IOConnection.cs @@ -273,6 +273,7 @@ namespace ModernKeePassLib.Serialization public static readonly string WrhMoveFileTo = "MoveFileTo"; public static event EventHandler IOAccessPre; + public static event EventHandler IOWebRequestPre; #if !ModernKeePassLib && !KeePassLibSD // Allow self-signed certificates, expired certificates, etc. @@ -369,6 +370,13 @@ namespace ModernKeePassLib.Serialization bool? ob = p.GetBool(IocKnownProperties.PreAuth); if(ob.HasValue) request.PreAuthenticate = ob.Value; #endif + + if(IOConnection.IOWebRequestPre != null) + { + IOWebRequestEventArgs e = new IOWebRequestEventArgs(request, + ((ioc != null) ? ioc.CloneDeep() : null)); + IOConnection.IOWebRequestPre(null, e); + } } internal static void ConfigureWebClient(WebClient wc) @@ -549,13 +557,13 @@ namespace ModernKeePassLib.Serialization PrepareWebAccess(ioc); IOWebClient wc = new IOWebClient(ioc); - ConfigureWebClient(wc); if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0)) wc.Credentials = new NetworkCredential(ioc.UserName, ioc.Password); - else if(NativeLib.IsUnix()) // Mono requires credentials + else if(MonoWorkarounds.IsRequired(688007)) wc.Credentials = new NetworkCredential("anonymous", string.Empty); + ConfigureWebClient(wc); return wc; } @@ -564,13 +572,13 @@ namespace ModernKeePassLib.Serialization PrepareWebAccess(ioc); WebRequest req = WebRequest.Create(ioc.Path); - ConfigureWebRequest(req, ioc); if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0)) req.Credentials = new NetworkCredential(ioc.UserName, ioc.Password); - else if(NativeLib.IsUnix()) // Mono requires credentials + else if(MonoWorkarounds.IsRequired(688007)) req.Credentials = new NetworkCredential("anonymous", string.Empty); + ConfigureWebRequest(req, ioc); return req; } diff --git a/ModernKeePassLib/Serialization/IOConnectionInfo.cs b/ModernKeePassLib/Serialization/IOConnectionInfo.cs index 2ceb78c..cc8b80e 100644 --- a/ModernKeePassLib/Serialization/IOConnectionInfo.cs +++ b/ModernKeePassLib/Serialization/IOConnectionInfo.cs @@ -1,6 +1,6 @@ /* KeePass Password Safe - The Open-Source Password Manager - Copyright (C) 2003-2018 Dominik Reichl + Copyright (C) 2003-2020 Dominik Reichl 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 @@ -336,7 +336,7 @@ namespace ModernKeePassLib.Serialization #if ModernKeePassLib if (IsLocalFile()) return Bytes != null; #else - if(IsLocalFile()) return File.Exists(m_strUrl); + return IOConnection.FileExists(this, false); // Raises event #endif return true; diff --git a/ModernKeePassLib/Utility/MemUtil.cs b/ModernKeePassLib/Utility/MemUtil.cs index d44e423..820c8c9 100644 --- a/ModernKeePassLib/Utility/MemUtil.cs +++ b/ModernKeePassLib/Utility/MemUtil.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; #if KeePassLibSD @@ -820,6 +821,52 @@ namespace ModernKeePassLib.Utility IDisposable d = (o as IDisposable); if(d != null) d.Dispose(); } + + internal static T BytesToStruct(byte[] pb, int iOffset) + where T : struct + { + if(pb == null) throw new ArgumentNullException("pb"); + if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset"); + + int cb = Marshal.SizeOf(typeof(T)); + if(cb <= 0) { Debug.Assert(false); return default(T); } + + if(iOffset > (pb.Length - cb)) throw new ArgumentOutOfRangeException("iOffset"); + + IntPtr p = Marshal.AllocCoTaskMem(cb); + if(p == IntPtr.Zero) throw new OutOfMemoryException(); + + object o; + try + { + Marshal.Copy(pb, iOffset, p, cb); + o = Marshal.PtrToStructure(p, typeof(T)); + } + finally { Marshal.FreeCoTaskMem(p); } + + return (T)o; + } + + internal static byte[] StructToBytes(ref T t) + where T : struct + { + int cb = Marshal.SizeOf(typeof(T)); + if(cb <= 0) { Debug.Assert(false); return MemUtil.EmptyByteArray; } + + byte[] pb = new byte[cb]; + + IntPtr p = Marshal.AllocCoTaskMem(cb); + if(p == IntPtr.Zero) throw new OutOfMemoryException(); + + try + { + Marshal.StructureToPtr(t, p, false); + Marshal.Copy(p, pb, 0, cb); + } + finally { Marshal.FreeCoTaskMem(p); } + + return pb; + } } internal sealed class ArrayHelperEx : IEqualityComparer, IComparer diff --git a/ModernKeePassLib/Utility/MessageService.cs b/ModernKeePassLib/Utility/MessageService.cs index 4632f61..e888b28 100644 --- a/ModernKeePassLib/Utility/MessageService.cs +++ b/ModernKeePassLib/Utility/MessageService.cs @@ -180,6 +180,8 @@ namespace ModernKeePassLib.Utility internal static DialogResult SafeShowMessageBox(string strText, string strTitle, MessageBoxButtons mb, MessageBoxIcon mi, MessageBoxDefaultButton mdb) { + // strText += MessageService.NewParagraph + (new StackTrace(true)).ToString(); + #if KeePassLibSD return MessageBox.Show(strText, strTitle, mb, mi, mdb); #else diff --git a/ModernKeePassLib/Utility/MonoWorkarounds.cs b/ModernKeePassLib/Utility/MonoWorkarounds.cs index 6859a00..24a1ea5 100644 --- a/ModernKeePassLib/Utility/MonoWorkarounds.cs +++ b/ModernKeePassLib/Utility/MonoWorkarounds.cs @@ -119,10 +119,12 @@ namespace ModernKeePassLib.Utility // 2140: // Explicit control focusing is ignored. // https://sourceforge.net/p/keepass/feature-requests/2140/ - // 5795: + // 5795: [Fixed] // Text in input field is incomplete. // https://bugzilla.xamarin.com/show_bug.cgi?id=5795 // https://sourceforge.net/p/keepass/discussion/329220/thread/d23dc88b/ + // https://github.com/mono/mono/commit/1a79065f8cd9f128e6e527e5d573111f794ce288 + // https://github.com/mono/mono/pull/5947 // 9604: // Trying to resolve a non-existing metadata token crashes Mono. // https://github.com/mono/mono/issues/9604 @@ -162,6 +164,10 @@ namespace ModernKeePassLib.Utility // 686017: // Minimum sizes must be enforced. // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=686017 + // 688007: [Fixed] + // Credentials are required for anonymous web requests. + // https://bugzilla.novell.com/show_bug.cgi?id=688007 + // https://sourceforge.net/p/keepass/bugs/1950/ // 801414: // Mono recreates the main window incorrectly. // https://bugs.launchpad.net/ubuntu/+source/keepass2/+bug/801414 @@ -192,13 +198,21 @@ namespace ModernKeePassLib.Utility if(g_dForceReq.TryGetValue(uBugID, out bForce)) return bForce; ulong v = NativeLib.MonoVersion; - if(v != 0) + if(v == 0) return true; + + bool b = true; + switch(uBugID) { - if(uBugID == 10163) - return (v >= 0x0002000B00000000UL); // >= 2.11 + case 5795: + b = (v < 0x0005000A00000000UL); break; + case 10163: + b = (v >= 0x0002000B00000000UL); break; + case 688007: + b = (v < 0x0006000000000000UL); break; + default: break; } - return true; + return b; } internal static void SetEnabled(string strIDs, bool bEnabled) diff --git a/ModernKeePassLib/Utility/StrUtil.cs b/ModernKeePassLib/Utility/StrUtil.cs index 6169ee3..0bb57c0 100644 --- a/ModernKeePassLib/Utility/StrUtil.cs +++ b/ModernKeePassLib/Utility/StrUtil.cs @@ -320,6 +320,18 @@ namespace ModernKeePassLib.Utility return str; } + internal static string RtfFilterText(string strText) + { + if(strText == null) { Debug.Assert(false); return string.Empty; } + + // A U+FFFC character causes the rest of the text to be lost. + // With '?', the string length, substring indices and + // character visibility remain the same. + // More special characters (unproblematic) in rich text boxes: + // https://docs.microsoft.com/en-us/windows/win32/api/richedit/ns-richedit-gettextex + return strText.Replace('\uFFFC', '?'); + } + internal static bool ContainsHighChar(string str) { if(str == null) { Debug.Assert(false); return false; } @@ -975,35 +987,25 @@ namespace ModernKeePassLib.Utility public static string AddAccelerator(string strMenuText, List lAvailKeys) { - if(strMenuText == null) { Debug.Assert(false); return null; } + if(strMenuText == null) { Debug.Assert(false); return string.Empty; } if(lAvailKeys == null) { Debug.Assert(false); return strMenuText; } int xa = -1, xs = 0; for(int i = 0; i < strMenuText.Length; ++i) { - char ch = strMenuText[i]; + char ch = char.ToLowerInvariant(strMenuText[i]); -#if KeePassLibSD - char chUpper = char.ToUpper(ch); -#else - char chUpper = char.ToUpperInvariant(ch); -#endif - xa = lAvailKeys.IndexOf(chUpper); - if(xa >= 0) { xs = i; break; } - -#if KeePassLibSD - char chLower = char.ToLower(ch); -#else - char chLower = char.ToLowerInvariant(ch); -#endif - xa = lAvailKeys.IndexOf(chLower); - if(xa >= 0) { xs = i; break; } + for(int j = 0; j < lAvailKeys.Count; ++j) + { + if(char.ToLowerInvariant(lAvailKeys[j]) == ch) + { + lAvailKeys.RemoveAt(j); + return strMenuText.Insert(i, @"&"); + } + } } - if(xa < 0) return strMenuText; - - lAvailKeys.RemoveAt(xa); - return strMenuText.Insert(xs, @"&"); + return strMenuText; } public static string EncodeMenuText(string strText) diff --git a/ModernKeePassLib/Utility/UrlUtil.cs b/ModernKeePassLib/Utility/UrlUtil.cs index 6982d33..53e11ef 100644 --- a/ModernKeePassLib/Utility/UrlUtil.cs +++ b/ModernKeePassLib/Utility/UrlUtil.cs @@ -287,13 +287,13 @@ namespace ModernKeePassLib.Utility if(strUrl.Length == 0) { Debug.Assert(false); return string.Empty; } #if !ModernKeePassLib - if(!strUrl.StartsWith(Uri.UriSchemeFile + ":", StrUtil.CaseIgnoreCmp)) + if(!strUrl.StartsWith(Uri.UriSchemeFile + ":", StrUtil.CaseIgnoreCmp)) { Debug.Assert(false); return strUrl; } #endif - try + try { Uri uri = new Uri(strUrl); string str = uri.LocalPath;