2
KeePass Password Safe - The Open-Source Password Manager
3
Copyright (C) 2003-2014 Dominik Reichl <dominik.reichl@t-online.de>
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.
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.
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
21
using System.Collections.Generic;
25
using System.Diagnostics;
27
#if (!KeePassLibSD && !KeePassRT)
28
using System.Net.Cache;
29
using System.Net.Security;
33
using System.Security.Cryptography.X509Certificates;
36
using KeePassLib.Native;
37
using KeePassLib.Utility;
39
namespace KeePassLib.Serialization
41
#if (!KeePassLibSD && !KeePassRT)
42
public sealed class IOWebClient : WebClient
44
protected override WebRequest GetWebRequest(Uri address)
46
WebRequest request = base.GetWebRequest(address);
47
IOConnection.ConfigureWebRequest(request);
53
public static class IOConnection
55
#if (!KeePassLibSD && !KeePassRT)
56
private static ProxyServerType m_pstProxyType = ProxyServerType.System;
57
private static string m_strProxyAddr = string.Empty;
58
private static string m_strProxyPort = string.Empty;
59
private static string m_strProxyUserName = string.Empty;
60
private static string m_strProxyPassword = string.Empty;
62
private static bool m_bSslCertsAcceptInvalid = false;
63
internal static bool SslCertsAcceptInvalid
65
// get { return m_bSslCertsAcceptInvalid; }
66
set { m_bSslCertsAcceptInvalid = value; }
70
// Web request methods
71
public const string WrmDeleteFile = "DELETEFILE";
72
public const string WrmMoveFile = "MOVEFILE";
74
// Web request headers
75
public const string WrhMoveFileTo = "MoveFileTo";
77
public static event EventHandler<IOAccessEventArgs> IOAccessPre;
79
#if (!KeePassLibSD && !KeePassRT)
80
// Allow self-signed certificates, expired certificates, etc.
81
private static bool AcceptCertificate(object sender,
82
X509Certificate certificate, X509Chain chain,
83
SslPolicyErrors sslPolicyErrors)
88
internal static void SetProxy(ProxyServerType pst, string strAddr,
89
string strPort, string strUserName, string strPassword)
92
m_strProxyAddr = (strAddr ?? string.Empty);
93
m_strProxyPort = (strPort ?? string.Empty);
94
m_strProxyUserName = (strUserName ?? string.Empty);
95
m_strProxyPassword = (strPassword ?? string.Empty);
98
internal static void ConfigureWebRequest(WebRequest request)
100
if(request == null) { Debug.Assert(false); return; } // No throw
103
if(request is HttpWebRequest)
105
request.PreAuthenticate = true; // Also auth GET
106
if(request.Method == WebRequestMethods.Http.Post)
107
request.Method = WebRequestMethods.Http.Put;
109
// else if(request is FtpWebRequest)
111
// Debug.Assert(((FtpWebRequest)request).UsePassive);
114
// Not implemented and ignored in Mono < 2.10
117
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
119
catch(NotImplementedException) { }
120
catch(Exception) { Debug.Assert(false); }
125
if(GetWebProxy(out prx)) request.Proxy = prx;
127
catch(Exception) { Debug.Assert(false); }
130
internal static void ConfigureWebClient(WebClient wc)
132
// Not implemented and ignored in Mono < 2.10
135
wc.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
137
catch(NotImplementedException) { }
138
catch(Exception) { Debug.Assert(false); }
143
if(GetWebProxy(out prx)) wc.Proxy = prx;
145
catch(Exception) { Debug.Assert(false); }
148
private static bool GetWebProxy(out IWebProxy prx)
152
if(m_pstProxyType == ProxyServerType.None)
153
return true; // Use null proxy
154
if(m_pstProxyType == ProxyServerType.Manual)
158
if(m_strProxyPort.Length > 0)
159
prx = new WebProxy(m_strProxyAddr, int.Parse(m_strProxyPort));
160
else prx = new WebProxy(m_strProxyAddr);
162
if((m_strProxyUserName.Length > 0) || (m_strProxyPassword.Length > 0))
163
prx.Credentials = new NetworkCredential(m_strProxyUserName,
166
return true; // Use manual proxy
168
catch(Exception exProxy)
170
string strInfo = m_strProxyAddr;
171
if(m_strProxyPort.Length > 0) strInfo += ":" + m_strProxyPort;
172
MessageService.ShowWarning(strInfo, exProxy.Message);
175
return false; // Use default
178
if((m_strProxyUserName.Length == 0) && (m_strProxyPassword.Length == 0))
179
return false; // Use default proxy, no auth
183
prx = WebRequest.DefaultWebProxy;
184
if(prx == null) prx = WebRequest.GetSystemWebProxy();
185
if(prx == null) throw new InvalidOperationException();
187
prx.Credentials = new NetworkCredential(m_strProxyUserName,
191
catch(Exception) { Debug.Assert(false); }
196
private static void PrepareWebAccess()
198
if(m_bSslCertsAcceptInvalid)
199
ServicePointManager.ServerCertificateValidationCallback =
200
IOConnection.AcceptCertificate;
202
ServicePointManager.ServerCertificateValidationCallback = null;
205
private static IOWebClient CreateWebClient(IOConnectionInfo ioc)
209
IOWebClient wc = new IOWebClient();
210
ConfigureWebClient(wc);
212
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
213
wc.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
214
else if(NativeLib.IsUnix()) // Mono requires credentials
215
wc.Credentials = new NetworkCredential("anonymous", string.Empty);
220
private static WebRequest CreateWebRequest(IOConnectionInfo ioc)
224
WebRequest req = WebRequest.Create(ioc.Path);
225
ConfigureWebRequest(req);
227
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
228
req.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
229
else if(NativeLib.IsUnix()) // Mono requires credentials
230
req.Credentials = new NetworkCredential("anonymous", string.Empty);
235
public static Stream OpenRead(IOConnectionInfo ioc)
237
RaiseIOAccessPreEvent(ioc, IOAccessType.Read);
239
if(StrUtil.IsDataUri(ioc.Path))
241
byte[] pbData = StrUtil.DataUriToData(ioc.Path);
242
if(pbData != null) return new MemoryStream(pbData, false);
245
if(ioc.IsLocalFile()) return OpenReadLocal(ioc);
247
return CreateWebClient(ioc).OpenRead(new Uri(ioc.Path));
250
public static Stream OpenRead(IOConnectionInfo ioc)
252
RaiseIOAccessPreEvent(ioc, IOAccessType.Read);
254
return OpenReadLocal(ioc);
258
private static Stream OpenReadLocal(IOConnectionInfo ioc)
260
return new FileStream(ioc.Path, FileMode.Open, FileAccess.Read,
264
#if (!KeePassLibSD && !KeePassRT)
265
public static Stream OpenWrite(IOConnectionInfo ioc)
267
if(ioc == null) { Debug.Assert(false); return null; }
269
RaiseIOAccessPreEvent(ioc, IOAccessType.Write);
271
if(ioc.IsLocalFile()) return OpenWriteLocal(ioc);
273
Uri uri = new Uri(ioc.Path);
275
// Mono does not set HttpWebRequest.Method to POST for writes,
276
// so one needs to set the method to PUT explicitly
277
if(NativeLib.IsUnix() && (uri.Scheme.Equals(Uri.UriSchemeHttp,
278
StrUtil.CaseIgnoreCmp) || uri.Scheme.Equals(Uri.UriSchemeHttps,
279
StrUtil.CaseIgnoreCmp)))
280
return CreateWebClient(ioc).OpenWrite(uri, WebRequestMethods.Http.Put);
282
return CreateWebClient(ioc).OpenWrite(uri);
285
public static Stream OpenWrite(IOConnectionInfo ioc)
287
RaiseIOAccessPreEvent(ioc, IOAccessType.Write);
289
return OpenWriteLocal(ioc);
293
private static Stream OpenWriteLocal(IOConnectionInfo ioc)
295
return new FileStream(ioc.Path, FileMode.Create, FileAccess.Write,
299
public static bool FileExists(IOConnectionInfo ioc)
301
return FileExists(ioc, false);
304
public static bool FileExists(IOConnectionInfo ioc, bool bThrowErrors)
306
if(ioc == null) { Debug.Assert(false); return false; }
308
RaiseIOAccessPreEvent(ioc, IOAccessType.Exists);
310
if(ioc.IsLocalFile()) return File.Exists(ioc.Path);
312
#if (!KeePassLibSD && !KeePassRT)
313
if(ioc.Path.StartsWith("ftp://", StrUtil.CaseIgnoreCmp))
315
bool b = SendCommand(ioc, WebRequestMethods.Ftp.GetDateTimestamp);
316
if(!b && bThrowErrors) throw new InvalidOperationException();
323
Stream s = OpenRead(ioc);
324
if(s == null) throw new FileNotFoundException();
326
try { s.ReadByte(); }
329
// We didn't download the file completely; close may throw
330
// an exception -- that's okay
336
if(bThrowErrors) throw;
343
public static void DeleteFile(IOConnectionInfo ioc)
345
RaiseIOAccessPreEvent(ioc, IOAccessType.Delete);
347
if(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; }
349
#if (!KeePassLibSD && !KeePassRT)
350
WebRequest req = CreateWebRequest(ioc);
353
if(req is HttpWebRequest) req.Method = "DELETE";
354
else if(req is FtpWebRequest) req.Method = WebRequestMethods.Ftp.DeleteFile;
355
else if(req is FileWebRequest)
357
File.Delete(UrlUtil.FileUrlToPath(ioc.Path));
360
else req.Method = WrmDeleteFile;
362
DisposeResponse(req.GetResponse(), true);
368
/// Rename/move a file. For local file system and WebDAV, the
369
/// specified file is moved, i.e. the file destination can be
370
/// in a different directory/path. In contrast, for FTP the
371
/// file is renamed, i.e. its destination must be in the same
374
/// <param name="iocFrom">Source file path.</param>
375
/// <param name="iocTo">Target file path.</param>
376
public static void RenameFile(IOConnectionInfo iocFrom, IOConnectionInfo iocTo)
378
RaiseIOAccessPreEvent(iocFrom, iocTo, IOAccessType.Move);
380
if(iocFrom.IsLocalFile()) { File.Move(iocFrom.Path, iocTo.Path); return; }
382
#if (!KeePassLibSD && !KeePassRT)
383
WebRequest req = CreateWebRequest(iocFrom);
386
if(req is HttpWebRequest)
389
req.Headers.Set("Destination", iocTo.Path); // Full URL supported
391
else if(req is FtpWebRequest)
393
req.Method = WebRequestMethods.Ftp.Rename;
394
string strTo = UrlUtil.GetFileName(iocTo.Path);
396
// We're affected by .NET bug 621450:
397
// https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only
398
// Prepending "./", "%2E/" or "Dummy/../" doesn't work.
400
((FtpWebRequest)req).RenameTo = strTo;
402
else if(req is FileWebRequest)
404
File.Move(UrlUtil.FileUrlToPath(iocFrom.Path),
405
UrlUtil.FileUrlToPath(iocTo.Path));
410
req.Method = WrmMoveFile;
411
req.Headers.Set(WrhMoveFileTo, iocTo.Path);
414
DisposeResponse(req.GetResponse(), true);
418
// using(Stream sIn = IOConnection.OpenRead(iocFrom))
420
// using(Stream sOut = IOConnection.OpenWrite(iocTo))
422
// MemUtil.CopyStream(sIn, sOut);
428
// DeleteFile(iocFrom);
431
#if (!KeePassLibSD && !KeePassRT)
432
private static bool SendCommand(IOConnectionInfo ioc, string strMethod)
436
WebRequest req = CreateWebRequest(ioc);
437
req.Method = strMethod;
438
DisposeResponse(req.GetResponse(), true);
440
catch(Exception) { return false; }
446
private static void DisposeResponse(WebResponse wr, bool bGetStream)
448
if(wr == null) return;
454
Stream s = wr.GetResponseStream();
455
if(s != null) s.Close();
458
catch(Exception) { Debug.Assert(false); }
461
catch(Exception) { Debug.Assert(false); }
464
public static byte[] ReadFile(IOConnectionInfo ioc)
467
MemoryStream ms = null;
470
sIn = IOConnection.OpenRead(ioc);
471
if(sIn == null) return null;
473
ms = new MemoryStream();
474
MemUtil.CopyStream(sIn, ms);
481
if(sIn != null) sIn.Close();
482
if(ms != null) ms.Close();
488
private static void RaiseIOAccessPreEvent(IOConnectionInfo ioc, IOAccessType t)
490
RaiseIOAccessPreEvent(ioc, null, t);
493
private static void RaiseIOAccessPreEvent(IOConnectionInfo ioc,
494
IOConnectionInfo ioc2, IOAccessType t)
496
if(ioc == null) { Debug.Assert(false); return; }
499
if(IOConnection.IOAccessPre != null)
501
IOConnectionInfo ioc2Lcl = ((ioc2 != null) ? ioc2.CloneDeep() : null);
502
IOAccessEventArgs e = new IOAccessEventArgs(ioc.CloneDeep(), ioc2Lcl, t);
503
IOConnection.IOAccessPre(null, e);