Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/duplicati/duplicati.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs')
-rw-r--r--Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs333
1 files changed, 203 insertions, 130 deletions
diff --git a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
index a96df6616..032854043 100644
--- a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
+++ b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
@@ -44,6 +44,9 @@ namespace Duplicati.Library.Backend.AlternativeFTP
private const string CONFIG_KEY_AFTP_DATA_CONNECTION_TYPE = "aftp-data-connection-type";
private const string CONFIG_KEY_AFTP_SSL_PROTOCOLS = "aftp-ssl-protocols";
+ private const string TEST_FILE_NAME = "duplicati-access-privileges-test.tmp";
+ private const string TEST_FILE_CONTENT = "This file used by Duplicati to test access permissions and could be safely deleted.";
+
// ReSharper disable InconsistentNaming
private static readonly string DEFAULT_DATA_CONNECTION_TYPE_STRING = DEFAULT_DATA_CONNECTION_TYPE.ToString();
private static readonly string DEFAULT_ENCRYPTION_MODE_STRING = DEFAULT_ENCRYPTION_MODE.ToString();
@@ -76,18 +79,21 @@ namespace Duplicati.Library.Backend.AlternativeFTP
get { return "aftp"; }
}
+ private FtpClient Client
+ { get; set; }
+
public IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
- new CommandLineArgument("auth-password", CommandLineArgument.ArgumentType.Password, Strings.DescriptionAuthPasswordShort, Strings.DescriptionAuthPasswordLong),
- new CommandLineArgument("auth-username", CommandLineArgument.ArgumentType.String, Strings.DescriptionAuthUsernameShort, Strings.DescriptionAuthUsernameLong),
- new CommandLineArgument("disable-upload-verify", CommandLineArgument.ArgumentType.Boolean, Strings.DescriptionDisableUploadVerifyShort, Strings.DescriptionDisableUploadVerifyLong),
- new CommandLineArgument(CONFIG_KEY_AFTP_DATA_CONNECTION_TYPE, CommandLineArgument.ArgumentType.Enumeration, Strings.DescriptionFtpDataConnectionTypeShort, Strings.DescriptionFtpDataConnectionTypeLong, DEFAULT_DATA_CONNECTION_TYPE_STRING, null, Enum.GetNames(typeof(FtpDataConnectionType))),
- new CommandLineArgument(CONFIG_KEY_AFTP_ENCRYPTION_MODE, CommandLineArgument.ArgumentType.Enumeration, Strings.DescriptionFtpEncryptionModeShort, Strings.DescriptionFtpEncryptionModeLong, DEFAULT_ENCRYPTION_MODE_STRING, null, Enum.GetNames(typeof(FtpEncryptionMode))),
- new CommandLineArgument(CONFIG_KEY_AFTP_SSL_PROTOCOLS, CommandLineArgument.ArgumentType.Flags, Strings.DescriptionSslProtocolsShort, Strings.DescriptionSslProtocolsLong, DEFAULT_SSL_PROTOCOLS_STRING, null, Enum.GetNames(typeof(SslProtocols))),
- });
+ new CommandLineArgument("auth-password", CommandLineArgument.ArgumentType.Password, Strings.DescriptionAuthPasswordShort, Strings.DescriptionAuthPasswordLong),
+ new CommandLineArgument("auth-username", CommandLineArgument.ArgumentType.String, Strings.DescriptionAuthUsernameShort, Strings.DescriptionAuthUsernameLong),
+ new CommandLineArgument("disable-upload-verify", CommandLineArgument.ArgumentType.Boolean, Strings.DescriptionDisableUploadVerifyShort, Strings.DescriptionDisableUploadVerifyLong),
+ new CommandLineArgument(CONFIG_KEY_AFTP_DATA_CONNECTION_TYPE, CommandLineArgument.ArgumentType.Enumeration, Strings.DescriptionFtpDataConnectionTypeShort, Strings.DescriptionFtpDataConnectionTypeLong, DEFAULT_DATA_CONNECTION_TYPE_STRING, null, Enum.GetNames(typeof(FtpDataConnectionType))),
+ new CommandLineArgument(CONFIG_KEY_AFTP_ENCRYPTION_MODE, CommandLineArgument.ArgumentType.Enumeration, Strings.DescriptionFtpEncryptionModeShort, Strings.DescriptionFtpEncryptionModeLong, DEFAULT_ENCRYPTION_MODE_STRING, null, Enum.GetNames(typeof(FtpEncryptionMode))),
+ new CommandLineArgument(CONFIG_KEY_AFTP_SSL_PROTOCOLS, CommandLineArgument.ArgumentType.Flags, Strings.DescriptionSslProtocolsShort, Strings.DescriptionSslProtocolsLong, DEFAULT_SSL_PROTOCOLS_STRING, null, Enum.GetNames(typeof(SslProtocols))),
+ });
}
}
@@ -188,49 +194,48 @@ namespace Duplicati.Library.Backend.AlternativeFTP
}
}
- public List<IFileEntry> List()
+ public IEnumerable<IFileEntry> List()
{
return List("");
}
- public List<IFileEntry> List(string filename)
+ public IEnumerable<IFileEntry> List(string filename)
{
return List(filename, false);
}
- private List<IFileEntry> List(string filename, bool stripFile)
+ private IEnumerable<IFileEntry> List(string filename, bool stripFile)
{
var list = new List<IFileEntry>();
string remotePath = filename;
try
{
- using (var ftpClient = CreateClient())
- {
- var url = new Uri(_url);
+ var ftpClient = CreateClient();
- // Get the remote path
- remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
+ // Get the remote path
+ var url = new Uri(this._url);
+ remotePath = "/" + (url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath);
- if (!string.IsNullOrEmpty(filename))
+ if (!string.IsNullOrEmpty(filename))
+ {
+ if (!stripFile)
{
- if (!stripFile)
- {
- // Append the filename
- remotePath += "/" + filename;
- }
- else if (filename.Contains("/"))
- {
- remotePath += filename.Substring(0, filename.LastIndexOf("/", StringComparison.InvariantCulture));
- }
- // else: stripping the filename in this case ignoring it
+ // Append the filename
+ remotePath += filename;
+ }
+ else if (filename.Contains("/"))
+ {
+ remotePath += filename.Substring(0, filename.LastIndexOf("/", StringComparison.Ordinal));
}
+ // else: stripping the filename in this case ignoring it
+ }
- foreach (FtpListItem item in ftpClient.GetListing(remotePath, FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks))
+ foreach (FtpListItem item in ftpClient.GetListing(remotePath, FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks))
+ {
+ switch (item.Type)
{
- switch (item.Type)
- {
- case FtpFileSystemObjectType.Directory:
+ case FtpFileSystemObjectType.Directory:
{
if (item.Name == "." || item.Name == "..")
{
@@ -244,13 +249,13 @@ namespace Duplicati.Library.Backend.AlternativeFTP
break;
}
- case FtpFileSystemObjectType.File:
+ case FtpFileSystemObjectType.File:
{
list.Add(new FileEntry(item.Name, item.Size, new DateTime(), item.Modified));
break;
}
- case FtpFileSystemObjectType.Link:
+ case FtpFileSystemObjectType.Link:
{
if (item.Name == "." || item.Name == "..")
{
@@ -262,30 +267,30 @@ namespace Duplicati.Library.Backend.AlternativeFTP
switch (item.LinkObject.Type)
{
case FtpFileSystemObjectType.Directory:
- {
- if (item.Name == "." || item.Name == "..")
{
- continue;
- }
+ if (item.Name == "." || item.Name == "..")
+ {
+ continue;
+ }
- list.Add(new FileEntry(item.Name, -1, new DateTime(), item.Modified)
- {
- IsFolder = true,
- });
+ list.Add(new FileEntry(item.Name, -1, new DateTime(), item.Modified)
+ {
+ IsFolder = true,
+ });
- break;
- }
+ break;
+ }
case FtpFileSystemObjectType.File:
- {
- list.Add(new FileEntry(item.Name, item.Size, new DateTime(), item.Modified));
+ {
+ list.Add(new FileEntry(item.Name, item.Size, new DateTime(), item.Modified));
- break;
- }
+ break;
+ }
}
}
break;
}
- }
+
}
}
}// Message "Directory not found." string
@@ -309,42 +314,40 @@ namespace Duplicati.Library.Backend.AlternativeFTP
try
{
- using (var ftpClient = CreateClient())
+ var ftpClient = CreateClient();
+
+ try
+ {
+ streamLen = input.Length;
+ }
+ // ReSharper disable once EmptyGeneralCatchClause
+ catch
{
- try
- {
- streamLen = input.Length;
- }
- // ReSharper disable once EmptyGeneralCatchClause
- catch
- {
- }
+ }
- var url = new Uri(_url);
+ // Get the remote path
+ remotePath = "";
- // Get the remote path
- remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
+ if (!string.IsNullOrEmpty(remotename))
+ {
+ // Append the filename
+ remotePath += remotename;
+ }
- if (!string.IsNullOrEmpty(remotename))
+ using (var outputStream = ftpClient.OpenWrite(remotePath))
+ {
+ try
{
- // Append the filename
- remotePath += "/" + remotename;
+ CoreUtility.CopyStream(input, outputStream, true, _copybuffer);
}
-
- using (var outputStream = ftpClient.OpenWrite(remotePath))
+ finally
{
- try
- {
- CoreUtility.CopyStream(input, outputStream, true, _copybuffer);
- }
- finally
- {
- outputStream.Close();
- }
+ outputStream.Close();
}
}
+
if (_listVerify)
{
var fileEntries = List(remotename, true);
@@ -358,11 +361,11 @@ namespace Duplicati.Library.Backend.AlternativeFTP
return;
}
- throw new Exception(Strings.ListVerifySizeFailure(remotename, fileEntry.Size, streamLen));
+ throw new UserInformationException(Strings.ListVerifySizeFailure(remotename, fileEntry.Size, streamLen));
}
}
- throw new Exception(Strings.ListVerifyFailure(remotename, fileEntries.Select(n => n.Name)));
+ throw new UserInformationException(Strings.ListVerifyFailure(remotename, fileEntries.Select(n => n.Name)));
}
}
catch (FtpCommandException ex)
@@ -386,31 +389,29 @@ namespace Duplicati.Library.Backend.AlternativeFTP
public void Get(string remotename, System.IO.Stream output)
{
- using (var ftpClient = CreateClient())
- {
- var url = new Uri(_url);
+ var ftpClient = CreateClient();
- // Get the remote path
- var remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
+ // Get the remote path
+ var remotePath = "";
- if (!string.IsNullOrEmpty(remotename))
+ if (!string.IsNullOrEmpty(remotename))
+ {
+ // Append the filename
+ remotePath += remotename;
+ }
+
+ using (var inputStream = ftpClient.OpenRead(remotePath))
+ {
+ try
{
- // Append the filename
- remotePath += "/" + remotename;
+ CoreUtility.CopyStream(inputStream, output, false, _copybuffer);
}
-
- using (var inputStream = ftpClient.OpenRead(remotePath))
+ finally
{
- try
- {
- CoreUtility.CopyStream(inputStream, output, false, _copybuffer);
- }
- finally
- {
- inputStream.Close();
- }
+ inputStream.Close();
}
}
+
}
public void Get(string remotename, string localname)
@@ -423,21 +424,19 @@ namespace Duplicati.Library.Backend.AlternativeFTP
public void Delete(string remotename)
{
- using (var ftpClient = CreateClient())
- {
- var url = new Uri(_url);
+ var ftpClient = CreateClient();
- // Get the remote path
- var remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
+ // Get the remote path
+ var remotePath = "";
- if (!string.IsNullOrEmpty(remotename))
- {
- // Append the filename
- remotePath += "/" + remotename;
- }
-
- ftpClient.DeleteFile(remotePath);
+ if (!string.IsNullOrEmpty(remotename))
+ {
+ // Append the filename
+ remotePath += remotename;
}
+
+ ftpClient.DeleteFile(remotePath);
+
}
/// <summary>
@@ -451,50 +450,124 @@ namespace Duplicati.Library.Backend.AlternativeFTP
}
}
- public void Test()
+ private static System.IO.Stream StringToStream(string str)
{
- List();
+ var stream = new System.IO.MemoryStream();
+ var writer = new System.IO.StreamWriter(stream) { AutoFlush = true };
+ writer.Write(str);
+ return stream;
}
- public void CreateFolder()
+ /// <summary>
+ /// Test FTP access permissions.
+ /// </summary>
+ public void Test()
{
- using (var client = CreateClient())
+ var list = List();
+
+ // Delete test file if exists
+ if (list.Any(entry => entry.Name == TEST_FILE_NAME))
{
- var url = new Uri(_url);
+ try
+ {
+ Delete(TEST_FILE_NAME);
+ }
+ catch (Exception e)
+ {
+ throw new Exception(string.Format(Strings.ErrorDeleteFile, e.Message), e);
+ }
+ }
- // Get the remote path
- var remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
+ // Test write permissions
+ using (var testStream = StringToStream(TEST_FILE_CONTENT))
+ {
+ try
+ {
+ Put(TEST_FILE_NAME, testStream);
+ }
+ catch (Exception e)
+ {
+ throw new Exception(string.Format(Strings.ErrorWriteFile, e.Message), e);
+ }
+ }
- // Try to create the directory
- client.CreateDirectory(remotePath, true);
+ // Test read permissions
+ using (var stream = new System.IO.MemoryStream())
+ {
+ try
+ {
+ Get(TEST_FILE_NAME, stream);
+ }
+ catch (Exception e)
+ {
+ throw new Exception(string.Format(Strings.ErrorReadFile, e.Message), e);
+ }
+ }
+
+ // Cleanup
+ try
+ {
+ Delete(TEST_FILE_NAME);
+ }
+ catch (Exception e)
+ {
+ throw new Exception(string.Format(Strings.ErrorDeleteFile, e.Message), e);
}
}
+ public void CreateFolder()
+ {
+ var client = CreateClient();
+
+ var url = new Uri(_url);
+
+ // Get the remote path
+ var remotePath = url.AbsolutePath.EndsWith("/") ? url.AbsolutePath.Substring(0, url.AbsolutePath.Length - 1) : url.AbsolutePath;
+
+ // Try to create the directory
+ client.CreateDirectory(remotePath, true);
+
+ }
+
public void Dispose()
{
+ if (Client != null)
+ Client.Dispose();
+
+ Client = null;
_userInfo = null;
}
private FtpClient CreateClient()
{
- var url = _url;
+ if (this.Client == null) // Create connection if it doesn't exist yet
+ {
- var uri = new Uri(url);
+ var url = _url;
- var ftpClient = new FtpClient
- {
- Host = uri.Host,
- Port = uri.Port == -1 ? 21 : uri.Port,
- Credentials = _userInfo,
- EncryptionMode = _encryptionMode,
- DataConnectionType = _dataConnectionType,
- SslProtocols = _sslProtocols,
- EnableThreadSafeDataConnections = true, // Required to work properly but can result in up to 3 connections being used even when you expect just one..
- };
-
- ftpClient.ValidateCertificate += HandleValidateCertificate;
-
- return ftpClient;
+ var uri = new Uri(url);
+
+ var ftpClient = new FtpClient
+ {
+ Host = uri.Host,
+ Port = uri.Port == -1 ? 21 : uri.Port,
+ Credentials = _userInfo,
+ EncryptionMode = _encryptionMode,
+ DataConnectionType = _dataConnectionType,
+ SslProtocols = _sslProtocols,
+ EnableThreadSafeDataConnections = true, // Required to work properly but can result in up to 3 connections being used even when you expect just one..
+ };
+
+ ftpClient.ValidateCertificate += HandleValidateCertificate;
+
+ // Get the remote path
+ var remotePath = uri.AbsolutePath.EndsWith("/") ? uri.AbsolutePath.Substring(0, uri.AbsolutePath.Length - 1) : uri.AbsolutePath;
+ ftpClient.SetWorkingDirectory(remotePath);
+
+ this.Client = ftpClient;
+ } // else reuse existing connection
+
+ return this.Client;
}
private void HandleValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
@@ -510,7 +583,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP
var certHash = (_validHashes != null && _validHashes.Length > 0) ? CoreUtility.ByteArrayAsHexString(e.Certificate.GetCertHash()) : null;
if (certHash != null)
{
- if (_validHashes.Any(hash => !string.IsNullOrEmpty(hash) && certHash.Equals(hash, StringComparison.InvariantCultureIgnoreCase)))
+ if (_validHashes.Any(hash => !string.IsNullOrEmpty(hash) && certHash.Equals(hash, StringComparison.OrdinalIgnoreCase)))
{
e.Accept = true;
}