diff --git a/Duplicati CommandLine Only.sln b/Duplicati CommandLine Only.sln
index 8d217c713..60f91ed27 100644
--- a/Duplicati CommandLine Only.sln
+++ b/Duplicati CommandLine Only.sln
@@ -45,8 +45,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.A
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Localization", "Duplicati\Library\Localization\Duplicati.Library.Localization.csproj", "{B68F2214-951F-4F78-8488-66E1ED3F50BF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.AmazonCloudDrive", "Duplicati\Library\Backend\AmazonCloudDrive\Duplicati.Library.Backend.AmazonCloudDrive.csproj", "{08D7E42D-285C-4010-9881-986125FE2F3E}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.AutoUpdater", "Duplicati\Library\AutoUpdater\Duplicati.Library.AutoUpdater.csproj", "{7E119745-1F62-43F0-936C-F312A1912C0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.SQLiteHelper", "Duplicati\Library\SQLiteHelper\Duplicati.Library.SQLiteHelper.csproj", "{2C838169-B187-4B09-8768-1C24C2521C8D}"
@@ -179,10 +177,6 @@ Global
{B68F2214-951F-4F78-8488-66E1ED3F50BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.Build.0 = Release|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Release|Any CPU.Build.0 = Release|Any CPU
{7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E119745-1F62-43F0-936C-F312A1912C0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/Duplicati.sln b/Duplicati.sln
index 35e1e1594..5066303ef 100644
--- a/Duplicati.sln
+++ b/Duplicati.sln
@@ -71,8 +71,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.H
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.RecoveryTool", "Duplicati\CommandLine\RecoveryTool\Duplicati.CommandLine.RecoveryTool.csproj", "{4A010589-76E6-4F05-A5C4-4598D5DF11F8}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.AmazonCloudDrive", "Duplicati\Library\Backend\AmazonCloudDrive\Duplicati.Library.Backend.AmazonCloudDrive.csproj", "{08D7E42D-285C-4010-9881-986125FE2F3E}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Backblaze", "Duplicati\Library\Backend\Backblaze\Duplicati.Library.Backend.Backblaze.csproj", "{61C43D61-4368-4942-84A3-1EB623F4EF2A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Mega", "Duplicati\Library\Backend\Mega\Duplicati.Library.Backend.Mega.csproj", "{6643A5AE-AB38-453F-ADCE-408E35A81A83}"
@@ -245,10 +243,6 @@ Global
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}.Release|Any CPU.Build.0 = Release|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}.Release|Any CPU.Build.0 = Release|Any CPU
{61C43D61-4368-4942-84A3-1EB623F4EF2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61C43D61-4368-4942-84A3-1EB623F4EF2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61C43D61-4368-4942-84A3-1EB623F4EF2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
index 47f42addf..9f61b68fe 100644
--- a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
+++ b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
@@ -126,10 +126,6 @@
{D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}Duplicati.Library.Backend.HubiC
-
- {08D7E42D-285C-4010-9881-986125FE2F3E}
- Duplicati.Library.Backend.AmazonCloudDrive
- {1BFAE226-8364-4086-825C-BB83F6F3EE4C}Duplicati.Library.Backend.CloudFiles
@@ -195,4 +191,4 @@
-->
-
\ No newline at end of file
+
diff --git a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
index af3ca4d8b..81425bcd4 100644
--- a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
+++ b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
@@ -132,10 +132,6 @@
{D60AD540-0E7D-40CE-83AE-D26E01FFE9B8}Duplicati.Library.Backend.HubiC
-
- {08D7E42D-285C-4010-9881-986125FE2F3E}
- Duplicati.Library.Backend.AmazonCloudDrive
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}Duplicati.Library.Backend.Backblaze
@@ -160,4 +156,4 @@
-
\ No newline at end of file
+
diff --git a/Duplicati/CommandLine/Duplicati.CommandLine.csproj b/Duplicati/CommandLine/Duplicati.CommandLine.csproj
index 621485f26..54f616323 100644
--- a/Duplicati/CommandLine/Duplicati.CommandLine.csproj
+++ b/Duplicati/CommandLine/Duplicati.CommandLine.csproj
@@ -203,10 +203,6 @@
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}Duplicati.CommandLine.RecoveryTool
-
- {08D7E42D-285C-4010-9881-986125FE2F3E}
- Duplicati.Library.Backend.AmazonCloudDrive
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}Duplicati.Library.Backend.Backblaze
@@ -256,4 +252,4 @@
-->
-
\ No newline at end of file
+
diff --git a/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj b/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
index eecbcc15e..5bbf41b2b 100644
--- a/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
+++ b/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
@@ -157,10 +157,6 @@
{10D2D1B7-C664-41D8-9B3A-00040C3D421B}Duplicati.Library.Main
-
- {08D7E42D-285C-4010-9881-986125FE2F3E}
- Duplicati.Library.Backend.AmazonCloudDrive
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}Duplicati.Library.Backend.Backblaze
@@ -193,4 +189,4 @@
-
\ No newline at end of file
+
diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj
index bf7f60013..d5ad3ea1a 100644
--- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj
+++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj
@@ -313,10 +313,6 @@
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}Duplicati.CommandLine.RecoveryTool
-
- {08D7E42D-285C-4010-9881-986125FE2F3E}
- Duplicati.Library.Backend.AmazonCloudDrive
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}Duplicati.Library.Backend.Backblaze
@@ -467,4 +463,4 @@ copy /Y '%USERPROFILE%\.nuget\packages\alphavss\1.4.0\lib\net45\AlphaVSS.*.dll'
-
\ No newline at end of file
+
diff --git a/Duplicati/Library/Backend/AmazonCloudDrive/AmzCD.cs b/Duplicati/Library/Backend/AmazonCloudDrive/AmzCD.cs
deleted file mode 100644
index b0c4e1d4a..000000000
--- a/Duplicati/Library/Backend/AmazonCloudDrive/AmzCD.cs
+++ /dev/null
@@ -1,678 +0,0 @@
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation; either version 2.1 of the
-// License, or (at your option) any later version.
-//
-// This library 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
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-using Duplicati.Library.Common.IO;
-using Duplicati.Library.Interface;
-using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Duplicati.Library.Backend.AmazonCloudDrive
-{
- // ReSharper disable once ClassNeverInstantiated.Global
- // This class is instantiated dynamically in the BackendLoader.
- public class AmzCD : IBackend, IStreamingBackend, IRenameEnabledBackend
- {
- private const string AUTHID_OPTION = "authid";
- private const string LABELS_OPTION = "amzcd-labels";
- private const string DELAY_OPTION = "amzcd-consistency-delay";
-
- private const string DEFAULT_LABELS = "duplicati,backup";
- private const string DEFAULT_DELAY = "30s";
-
- private const int PAGE_SIZE = 200;
-
- private const string CLOUDRIVE_MASTER_URL = "https://drive.amazonaws.com/drive/v1/account/endpoint";
- private const string CACHE_FILE_NAME_TEMPLATE = "dupl-amzcd-endp-{0}-cache.txt";
- private static readonly TimeSpan KEEP_CACHE_FILE_TIME = TimeSpan.FromDays(5);
-
- private const string CONTENT_KIND_FOLDER = "FOLDER";
- private const string CONTENT_KIND_FILE = "FILE";
-
- private EndpointInfo m_endPointInfo;
- private ResourceModel m_curdir;
-
- private readonly string m_path;
- private readonly string[] m_labels;
-
- private readonly string m_authid;
- private readonly OAuthHelper m_oauth;
- private Dictionary m_filecache;
- private readonly string m_userid;
- private readonly TimeSpan m_delayTimeSpan;
-
- private static readonly object m_waitUntilLock;
- private static readonly Dictionary m_waitUntilAuthId;
- private static Dictionary m_waitUntilRemotename;
-
- static AmzCD()
- {
- m_waitUntilLock = new object();
- m_waitUntilAuthId = new Dictionary();
- m_waitUntilRemotename = new Dictionary();
- }
-
- // ReSharper disable once UnusedMember.Global
- // This constructor is needed by the BackendLoader.
- public AmzCD()
- {
- }
-
- // ReSharper disable once UnusedMember.Global
- // This constructor is needed by the BackendLoader.
- public AmzCD(string url, Dictionary options)
- {
- var uri = new Utility.Uri(url);
- m_path = Util.AppendDirSeparator(uri.HostAndPath, "/");
-
- if (options.ContainsKey(AUTHID_OPTION))
- m_authid = options[AUTHID_OPTION];
-
- string labels = DEFAULT_LABELS;
- if (options.ContainsKey(LABELS_OPTION))
- labels = options[LABELS_OPTION];
-
- string delay = DEFAULT_DELAY;
- if (options.ContainsKey(DELAY_OPTION))
- delay = options[DELAY_OPTION];
-
- if (!string.IsNullOrWhiteSpace(labels))
- m_labels = labels.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
-
- if (string.IsNullOrWhiteSpace(delay))
- m_delayTimeSpan = new TimeSpan(0);
- else
- m_delayTimeSpan = Library.Utility.Timeparser.ParseTimeSpan(delay);
-
- m_oauth = new OAuthHelper(m_authid, this.ProtocolKey) { AutoAuthHeader = true };
- m_userid = m_authid.Split(new [] {":"}, StringSplitOptions.RemoveEmptyEntries).First();
- }
-
- private static void CleanWaitUntil()
- {
- lock (m_waitUntilLock)
- {
- DateTime now = DateTime.Now;
- m_waitUntilRemotename = m_waitUntilRemotename.Where(pair => pair.Value > now)
- .ToDictionary(pair => pair.Key, pair => pair.Value);
- }
- }
-
- private DateTime GetWaitUntil(string remotename)
- {
- lock (m_waitUntilLock)
- {
- CleanWaitUntil();
-
- DateTime result;
- if (string.IsNullOrEmpty(remotename))
- {
- if (m_waitUntilAuthId.TryGetValue(m_authid, out result))
- return result;
- }
- else
- {
- if (m_waitUntilRemotename.TryGetValue(remotename, out result))
- return result;
- }
-
- return DateTime.MinValue;
- }
- }
-
- private void SetWaitUntil(string remotename, DateTime value)
- {
- lock (m_waitUntilLock)
- {
- DateTime oldValue;
-
- if (!string.IsNullOrEmpty(remotename))
- {
- if (!m_waitUntilRemotename.TryGetValue(remotename, out oldValue) || value > oldValue)
- m_waitUntilRemotename[remotename] = value;
- }
-
- if (!m_waitUntilAuthId.TryGetValue(m_authid, out oldValue) || value > oldValue)
- m_waitUntilAuthId[m_authid] = value;
- }
- }
-
- private void EnforceConsistencyDelay(string remotename)
- {
- var wait = GetWaitUntil(remotename) - DateTime.Now;
-
- if (wait.Ticks > 0)
- Thread.Sleep(wait);
- }
-
- private string CacheFilePath { get { return SystemIO.IO_OS.PathCombine(Utility.TempFolder.SystemTempPath, string.Format(CACHE_FILE_NAME_TEMPLATE, m_userid)); } }
-
- private void RefreshMetadataAndContentUrl()
- {
- try
- {
- if (File.Exists(CacheFilePath) && (DateTime.Now - File.GetLastWriteTime(CacheFilePath)) < KEEP_CACHE_FILE_TIME)
- {
- m_endPointInfo = JsonConvert.DeserializeObject(File.ReadAllText(CacheFilePath));
- return;
- }
- }
- catch
- {
- }
-
- m_endPointInfo = m_oauth.GetJSONData(CLOUDRIVE_MASTER_URL);
-
- try
- {
- if (m_endPointInfo.CustomerExists && m_endPointInfo.ContentUrl != null && m_endPointInfo.MetadataUrl != null)
- File.WriteAllText(CacheFilePath, JsonConvert.SerializeObject(m_endPointInfo));
- }
- catch
- {
- }
- }
-
- public string ContentUrl
- {
- get
- {
- if (m_endPointInfo == null)
- RefreshMetadataAndContentUrl();
- while (m_endPointInfo.ContentUrl.EndsWith("/", StringComparison.Ordinal))
- m_endPointInfo.ContentUrl = m_endPointInfo.ContentUrl.Substring(0, m_endPointInfo.ContentUrl.Length - 1);
- return m_endPointInfo.ContentUrl;
- }
- }
-
- public string MetadataUrl
- {
- get
- {
- if (m_endPointInfo == null)
- RefreshMetadataAndContentUrl();
- while (m_endPointInfo.MetadataUrl.EndsWith("/", StringComparison.Ordinal))
- m_endPointInfo.MetadataUrl = m_endPointInfo.MetadataUrl.Substring(0, m_endPointInfo.MetadataUrl.Length - 1);
- return m_endPointInfo.MetadataUrl;
- }
- }
-
- private Dictionary FileCache
- {
- get
- {
- if (m_filecache == null)
- List();
-
- return m_filecache;
- }
- }
-
- private ResourceModel GetCurrentDirectory(bool createMissing, string altpath = null)
- {
- var rootResponse = m_oauth.GetJSONData(string.Format("{0}/nodes?filters=isRoot:true", MetadataUrl));
- var parent = rootResponse.Data.First();
- var curpath = new List();
- foreach(var p in (altpath ?? m_path).Split(new string[] {"/"}, StringSplitOptions.RemoveEmptyEntries))
- {
- var requestUrl = string.Format("{0}/nodes?filters=kind:{1}%20and%20parents:{2}%20and%20name:{3}", MetadataUrl, CONTENT_KIND_FOLDER, Utility.Uri.UrlEncode(parent.ID), Utility.Uri.UrlPathEncode(EscapeFiltersValue(p)));
- var self = m_oauth.GetJSONData(requestUrl);
- if (self == null || self.Count == 0 || self.Data == null || self.Data.Length == 0)
- {
- if (!createMissing)
- throw new FolderMissingException(string.Format("Unable to find folder {0} in {1}", p, "/" + string.Join("/", curpath)));
-
- // Create the folder
- var data = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new CreateItemRequest() {
- Name = p,
- Parents = new string[] { parent.ID },
- Kind = CONTENT_KIND_FOLDER,
- Labels = m_labels
- }));
-
- parent = m_oauth.GetJSONData(
- string.Format("{0}/nodes", MetadataUrl),
-
- // Setup
- req =>
- {
- req.Method = "POST";
- req.ContentLength = data.Length;
- req.ContentType = "application/json";
- },
-
- // Post the folder entry
- req =>
- {
- using(var rs = req.GetRequestStream())
- rs.Write(data, 0, data.Length);
- }
- );
- SetWaitUntil(null, DateTime.Now + m_delayTimeSpan);
- }
- else if (self.Count > 1)
- throw new UserInformationException(Strings.AmzCD.MultipleEntries(p, "/" + string.Join("/", curpath)), "AmzCDMultipleEntries");
- else
- parent = self.Data.First();
- }
-
- return parent;
- }
-
- private ResourceModel CurrentDirectory
- {
- get
- {
- if (m_curdir == null)
- m_curdir = GetCurrentDirectory(false);
-
- return m_curdir;
- }
- }
-
- private string GetFileID(string name)
- {
- var recent = m_filecache == null;
-
- string id;
- FileCache.TryGetValue(name, out id);
- if (!string.IsNullOrWhiteSpace(id))
- return id;
-
- // Reset to make sure it is actually missing
- if (!recent)
- {
- List();
-
- FileCache.TryGetValue(name, out id);
- if (!string.IsNullOrWhiteSpace(id))
- return id;
- }
-
- throw new FileMissingException();
- }
-
- private static readonly System.Text.RegularExpressions.Regex FILTERS_VALUE_ESCAPECHAR = new System.Text.RegularExpressions.Regex(@"[+\-&|!(){}\[\]^'""~*?:\\ ]", System.Text.RegularExpressions.RegexOptions.Compiled);
-
- public static string EscapeFiltersValue(string value)
- {
- return FILTERS_VALUE_ESCAPECHAR.Replace(value, (m) => {
- return @"\" + m.Value;
- });
- }
-
- #region IStreamingBackend implementation
-
- public async Task PutAsync(string remotename, Stream stream, CancellationToken cancelToken)
- {
- EnforceConsistencyDelay(remotename);
-
- var overwrite = FileCache.ContainsKey(remotename);
- var fileid = overwrite ? m_filecache[remotename] : null;
-
- var url = string.Format(overwrite ? "{0}/nodes/{1}/content?suppress=deduplication" : "{0}/nodes?suppress=deduplication", ContentUrl, fileid);
-
- var createreq = new CreateItemRequest() {
- Name = remotename,
- Kind = CONTENT_KIND_FILE,
- Labels = m_labels,
- Parents = new string[] { CurrentDirectory.ID }
- };
-
- try
- {
- var item = await m_oauth.PostMultipartAndGetJSONDataAsync(
- url,
- req =>
- {
- req.Method = overwrite ? "PUT" : "POST";
- },
- cancelToken,
- new MultipartItem(createreq, "metadata"),
- new MultipartItem(stream, "content", remotename)
- ).ConfigureAwait(false);
-
- if (m_filecache != null)
- m_filecache[item.Name] = item.ID;
- }
- catch(Exception ex)
- {
- #if DEBUG
- if (ex is WebException exception)
- using(var sr = new StreamReader(exception.Response.GetResponseStream()))
- Console.WriteLine(sr.ReadToEnd());
- #endif
-
- m_filecache = null;
- throw;
- }
- finally
- {
- SetWaitUntil(remotename, DateTime.Now + m_delayTimeSpan);
- }
- }
-
- public void Get(string remotename, Stream stream)
- {
- EnforceConsistencyDelay(remotename);
-
- using (var resp = m_oauth.GetResponse(string.Format("{0}/nodes/{1}/content", ContentUrl, GetFileID(remotename))))
- using(var rs = Library.Utility.AsyncHttpRequest.TrySetTimeout(resp.GetResponseStream()))
- Utility.Utility.CopyStream(rs, stream);
- }
-
- #endregion
-
- #region IBackend implementation
-
- public IEnumerable List()
- {
- EnforceConsistencyDelay(null);
-
- var query = string.Format("{0}/nodes?filters=parents:{1}&limit={2}", MetadataUrl, Utility.Uri.UrlEncode(CurrentDirectory.ID), PAGE_SIZE);
- var res = new List();
- string nextToken = null;
- m_filecache = null;
- var cache = new Dictionary();
-
- do
- {
- var lst = m_oauth.GetJSONData(query + (string.IsNullOrWhiteSpace(nextToken) ? "" : ("&startToken=" + nextToken)));
- if (lst.Data != null)
- {
- foreach(var n in lst.Data)
- {
-
- if (string.Equals(CONTENT_KIND_FOLDER, n.Kind, StringComparison.OrdinalIgnoreCase))
- res.Add(new FileEntry(n.Name) { IsFolder = true });
- else if (string.Equals(CONTENT_KIND_FILE, n.Kind, StringComparison.OrdinalIgnoreCase))
- {
- cache[n.Name] = n.ID;
-
- if (n.ContentProperties == null)
- res.Add(new FileEntry(n.Name) {
- LastAccess = n.LastModified,
- LastModification = n.LastModified
- });
- else
- res.Add(new FileEntry(n.Name, n.ContentProperties.Size, n.LastModified, n.LastModified));
- }
- }
- }
-
- nextToken = lst.NextToken;
-
- // Contrary to the documentation, nextToken is null when the set is done
- if (lst.Count == 0)
- break;
-
- // Docs say to check for empty response ...
- //if (lst.Count < PAGE_SIZE)
- // break;
- } while(nextToken != null);
-
- m_filecache = cache;
- return res;
-
- }
-
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
- {
- using (FileStream fs = File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
- }
-
- public void Get(string remotename, string filename)
- {
- using (FileStream fs = File.Create(filename))
- Get(remotename, fs);
- }
-
- public void Delete(string remotename)
- {
- EnforceConsistencyDelay(remotename);
-
- try
- {
- using (m_oauth.GetResponse(string.Format("{0}/trash/{1}", MetadataUrl, GetFileID(remotename)), null, "PUT"))
- {
- }
-
- m_filecache.Remove(remotename);
- }
- catch
- {
- m_filecache = null;
- }
- SetWaitUntil(remotename, DateTime.Now + m_delayTimeSpan);
- }
-
- public void Test()
- {
- this.TestList();
- }
-
- public void CreateFolder()
- {
- EnforceConsistencyDelay(null);
- GetCurrentDirectory(true);
- }
-
- public string DisplayName
- {
- get
- {
- return Strings.AmzCD.DisplayName;
- }
- }
-
- public string ProtocolKey
- {
- get
- {
- return "amzcd";
- }
- }
-
- public IList SupportedCommands
- {
- get {
- return new List(new ICommandLineArgument[] {
- new CommandLineArgument(AUTHID_OPTION, CommandLineArgument.ArgumentType.Password, Strings.AmzCD.AuthidShort, Strings.AmzCD.AuthidLong(OAuthHelper.OAUTH_LOGIN_URL("amzcd"))),
- new CommandLineArgument(LABELS_OPTION, CommandLineArgument.ArgumentType.String, Strings.AmzCD.LabelsShort, Strings.AmzCD.LabelsLong, DEFAULT_LABELS),
- new CommandLineArgument(DELAY_OPTION, CommandLineArgument.ArgumentType.Timespan, Strings.AmzCD.DelayShort, Strings.AmzCD.DelayLong, DEFAULT_DELAY),
- });
- }
- }
-
- public string Description
- {
- get
- {
- return Strings.AmzCD.Description;
- }
- }
-
- public string[] DNSName
- {
- get
- {
- var contentUrl = string.Empty;
- var metdataUrl = string.Empty;
-
- if (m_endPointInfo != null)
- {
- if (!string.IsNullOrWhiteSpace(m_endPointInfo.ContentUrl))
- contentUrl = new Uri(m_endPointInfo.ContentUrl).Host;
- if (!string.IsNullOrWhiteSpace(m_endPointInfo.MetadataUrl))
- metdataUrl = new Uri(m_endPointInfo.MetadataUrl).Host;
- }
-
- return new string[] {
- new Uri(CLOUDRIVE_MASTER_URL).Host,
- contentUrl,
- metdataUrl
- };
- }
- }
-
- #endregion
-
- #region IDisposable implementation
-
- public void Dispose()
- {
- }
-
- #endregion
-
- #region IRenameEnabledBackend
-
- public void Rename(string oldname, string newname)
- {
- EnforceConsistencyDelay(oldname);
-
- var id = GetFileID(oldname);
-
- var data = System.Text.Encoding.UTF8.GetBytes(
- JsonConvert.SerializeObject(new CreateItemRequest() { Name = newname })
- );
-
- try
- {
- var resp = m_oauth.GetJSONData(
- string.Format("{0}/nodes/{1}", MetadataUrl, id),
- req =>
- {
- req.Method = "PATCH";
- req.ContentType = "application/json";
- req.ContentLength = data.Length;
- },
-
- req =>
- {
- using (var rs = req.GetRequestStream())
- rs.Write(data, 0, data.Length);
- }
- );
-
- m_filecache.Remove(oldname);
- m_filecache[newname] = resp.ID;
- }
- catch
- {
- m_filecache = null;
- throw;
- }
- finally
- {
- SetWaitUntil(oldname, DateTime.Now + m_delayTimeSpan);
- SetWaitUntil(newname, DateTime.Now + m_delayTimeSpan);
- }
- }
-
- #endregion
-
- #region JSON Classes
-
- private class ListResponse
- {
- [JsonProperty("count")]
- public long Count { get; set; }
- [JsonProperty("nextToken")]
- public string NextToken { get; set; }
- [JsonProperty("data")]
- public ResourceModel[] Data { get; set; }
- }
-
- private class ResourceModel
- {
- [JsonProperty("id")]
- public string ID { get; set; }
- [JsonProperty("name")]
- public string Name { get; set; }
- [JsonProperty("kind")]
- public string Kind { get; set; }
- [JsonProperty("version")]
- public long Version { get; set; }
- [JsonProperty("modifiedDate")]
- public DateTime LastModified { get; set; }
- [JsonProperty("createdDate")]
- public DateTime CreatedDate { get; set; }
- [JsonProperty("labels")]
- public string[] Labels { get; set; }
- [JsonProperty("description")]
- public string Description { get; set; }
- [JsonProperty("createdBy")]
- public string CreatedBy { get; set; }
- [JsonProperty("parents")]
- public string[] Parents { get; set; }
- [JsonProperty("status")]
- public string Status { get; set; }
- [JsonProperty("contentProperties")]
- public ContentProperties ContentProperties { get; set; }
- }
-
- private class ContentProperties
- {
- [JsonProperty("version")]
- public long Version { get; set; }
- [JsonProperty("md5")]
- public string MD5 { get; set; }
- [JsonProperty("size")]
- public long Size { get; set; }
-
- [JsonProperty("contentType")]
- public string ContentType { get; set; }
- [JsonProperty("extension")]
- public string Extension { get; set; }
- [JsonProperty("contentDate")]
- public DateTime ContentDate { get; set; }
- }
-
- private class EndpointInfo
- {
- [JsonProperty("customerExists")]
- public bool CustomerExists { get; set; }
- [JsonProperty("contentUrl")]
- public string ContentUrl { get; set; }
- [JsonProperty("metadataUrl")]
- public string MetadataUrl { get; set; }
- }
-
- private class CreateItemRequest
- {
- [JsonProperty("name")]
- public string Name { get; set; }
- [JsonProperty("kind")]
- public string Kind { get; set; }
- [JsonProperty("labels", NullValueHandling = NullValueHandling.Ignore)]
- public string[] Labels { get; set; }
- [JsonProperty("properties", NullValueHandling = NullValueHandling.Ignore)]
- public Dictionary Properties { get; set; }
- [JsonProperty("parents")]
- public string[] Parents { get; set; }
- }
-
- #endregion
- }
-}
-
diff --git a/Duplicati/Library/Backend/AmazonCloudDrive/Duplicati.Library.Backend.AmazonCloudDrive.csproj b/Duplicati/Library/Backend/AmazonCloudDrive/Duplicati.Library.Backend.AmazonCloudDrive.csproj
deleted file mode 100644
index 52413aaed..000000000
--- a/Duplicati/Library/Backend/AmazonCloudDrive/Duplicati.Library.Backend.AmazonCloudDrive.csproj
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
- Debug
- AnyCPU
- {08D7E42D-285C-4010-9881-986125FE2F3E}
- Library
- Duplicati.Library.Backend.AmazonCloudDrive
- Duplicati.Library.Backend.AmazonCloudDrive
- Duplicati.snk
- v4.6.2
- false
-
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- false
-
-
- full
- true
- bin\Release
- prompt
- 4
- false
-
-
-
- ..\..\..\..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
-
-
-
-
-
-
-
-
-
-
- {B68F2214-951F-4F78-8488-66E1ED3F50BF}
- Duplicati.Library.Localization
-
-
- {D4C37C33-5E73-4B56-B2C3-DC4A6BAA36BB}
- Duplicati.Library.OAuthHelper
-
-
- {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}
- Duplicati.Library.Utility
-
-
- {C5899F45-B0FF-483C-9D38-24A9FCAAB237}
- Duplicati.Library.Interface
-
-
- {D63E53E4-A458-4C2F-914D-92F715F58ACF}
- Duplicati.Library.Common
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Duplicati/Library/Backend/AmazonCloudDrive/Duplicati.snk b/Duplicati/Library/Backend/AmazonCloudDrive/Duplicati.snk
deleted file mode 100644
index e0c1e2dd8..000000000
Binary files a/Duplicati/Library/Backend/AmazonCloudDrive/Duplicati.snk and /dev/null differ
diff --git a/Duplicati/Library/Backend/AmazonCloudDrive/Properties/AssemblyInfo.cs b/Duplicati/Library/Backend/AmazonCloudDrive/Properties/AssemblyInfo.cs
deleted file mode 100644
index 47db1d9a2..000000000
--- a/Duplicati/Library/Backend/AmazonCloudDrive/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation; either version 2.1 of the
-// License, or (at your option) any later version.
-//
-// This library 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
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("Duplicati.Library.Backend.AmazonCloudDrive")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("kenneth")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("2.0.0.7")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
-
diff --git a/Duplicati/Library/Backend/AmazonCloudDrive/Strings.cs b/Duplicati/Library/Backend/AmazonCloudDrive/Strings.cs
deleted file mode 100644
index 8d90c01d1..000000000
--- a/Duplicati/Library/Backend/AmazonCloudDrive/Strings.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2015, The Duplicati Team
-// http://www.duplicati.com, info@duplicati.com
-//
-// This library is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation; either version 2.1 of the
-// License, or (at your option) any later version.
-//
-// This library 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
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-using Duplicati.Library.Localization.Short;
-
-namespace Duplicati.Library.Backend.Strings
-{
- internal static class AmzCD
- {
- public static string Description { get { return LC.L(@"This backend can read and write data to Amazon Cloud Drive. Supported format is ""amzcd://folder/subfolder""."); } }
- public static string AuthidShort { get { return LC.L(@"The authorization code"); } }
- public static string AuthidLong(string url) { return LC.L(@"The authorization token retrieved from {0}", url); }
- public static string DisplayName { get { return LC.L(@"Amazon Cloud Drive"); } }
- public static string LabelsShort { get { return LC.L(@"The labels to set"); } }
- public static string LabelsLong { get { return LC.L(@"Use this option to set labels on the files and folders created"); } }
- public static string MultipleEntries(string folder, string parent) { return LC.L(@"There is more than one item named ""{0}"" in the folder ""{1}""", folder, parent); }
- public static string DelayShort { get { return LC.L(@"The consistency delay"); } }
- public static string DelayLong { get { return LC.L(@"Amazon Cloud drive needs a small delay for results to stay consistent."); } }
- }
-}
-
diff --git a/Duplicati/Library/Backend/AmazonCloudDrive/packages.config b/Duplicati/Library/Backend/AmazonCloudDrive/packages.config
deleted file mode 100644
index 1812e5207..000000000
--- a/Duplicati/Library/Backend/AmazonCloudDrive/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/Duplicati/Library/Main/Controller.cs b/Duplicati/Library/Main/Controller.cs
index 684f19a67..e4c2b9273 100644
--- a/Duplicati/Library/Main/Controller.cs
+++ b/Duplicati/Library/Main/Controller.cs
@@ -803,10 +803,6 @@ namespace Duplicati.Library.Main
if (string.Equals(m_options.CompressionModule, "7z", StringComparison.OrdinalIgnoreCase))
Logging.Log.WriteWarningMessage(LOGTAG, "7zModuleHasIssues", null, "The 7z compression module has known issues and should only be used for experimental purposes");
- // Amazon CD is closing August 16th 2019
- if (string.Equals(new Library.Utility.Uri(m_backend).Scheme, "amzcd", StringComparison.OrdinalIgnoreCase))
- Logging.Log.WriteWarningMessage(LOGTAG, "AmzCDClosingApi", null, "The Amazon Cloud Drive API is closing down on August 16th 2019, please migrate your backups before this date");
-
//TODO: Based on the action, see if all options are relevant
}
diff --git a/Duplicati/Server/Duplicati.Server.csproj b/Duplicati/Server/Duplicati.Server.csproj
index 78f92106d..d0887dc34 100644
--- a/Duplicati/Server/Duplicati.Server.csproj
+++ b/Duplicati/Server/Duplicati.Server.csproj
@@ -291,10 +291,6 @@
{4A010589-76E6-4F05-A5C4-4598D5DF11F8}Duplicati.CommandLine.RecoveryTool
-
- {08D7E42D-285C-4010-9881-986125FE2F3E}
- Duplicati.Library.Backend.AmazonCloudDrive
- {61C43D61-4368-4942-84A3-1EB623F4EF2A}Duplicati.Library.Backend.Backblaze
@@ -338,4 +334,4 @@
-->
-
\ No newline at end of file
+
diff --git a/Duplicati/Server/webroot/ngax/scripts/directives/advancedOptionsEditor.js b/Duplicati/Server/webroot/ngax/scripts/directives/advancedOptionsEditor.js
index 176468929..22de2a242 100644
--- a/Duplicati/Server/webroot/ngax/scripts/directives/advancedOptionsEditor.js
+++ b/Duplicati/Server/webroot/ngax/scripts/directives/advancedOptionsEditor.js
@@ -26,7 +26,6 @@ backupApp.directive('advancedOptionsEditor', function() {
'throttle-download': 'speed',
'retry-delay': 'shorttimespan',
- 'amzcd-consistency-delay': 'shorttimespan',
'web-timeout': 'shorttimespan',
'run-script-timeout': 'shorttimespan'
};
diff --git a/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js b/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js
index 5ac789943..e428780f3 100644
--- a/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js
+++ b/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js
@@ -20,7 +20,6 @@ backupApp.service('EditUriBuiltins', function(AppService, AppUtils, SystemInfo,
EditUriBackendConfig.templates['onedrivev2'] = 'templates/backends/oauth.html';
EditUriBackendConfig.templates['sharepoint'] = 'templates/backends/sharepoint.html';
EditUriBackendConfig.templates['msgroup'] = 'templates/backends/msgroup.html';
- EditUriBackendConfig.templates['amzcd'] = 'templates/backends/amzcd.html';
EditUriBackendConfig.templates['openstack'] = 'templates/backends/openstack.html';
EditUriBackendConfig.templates['azure'] = 'templates/backends/azure.html';
EditUriBackendConfig.templates['gcs'] = 'templates/backends/gcs.html';
@@ -199,7 +198,6 @@ backupApp.service('EditUriBuiltins', function(AppService, AppUtils, SystemInfo,
EditUriBackendConfig.loaders['onedrivev2'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.loaders['sharepoint'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.loaders['msgroup'] = function() { return this['oauth-base'].apply(this, arguments); };
- EditUriBackendConfig.loaders['amzcd'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.loaders['box'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.loaders['dropbox'] = function() { return this['oauth-base'].apply(this, arguments); };
@@ -322,7 +320,6 @@ backupApp.service('EditUriBuiltins', function(AppService, AppUtils, SystemInfo,
EditUriBackendConfig.parsers['onedrive'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.parsers['onedrivev2'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.parsers['sharepoint'] = function() { return this['oauth-base'].apply(this, arguments); };
- EditUriBackendConfig.parsers['amzcd'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.parsers['box'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.parsers['dropbox'] = function() { return this['oauth-base'].apply(this, arguments); };
@@ -474,7 +471,6 @@ backupApp.service('EditUriBuiltins', function(AppService, AppUtils, SystemInfo,
EditUriBackendConfig.builders['onedrivev2'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.builders['sharepoint'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.builders['msgroup'] = function() { return this['oauth-base'].apply(this, arguments); };
- EditUriBackendConfig.builders['amzcd'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.builders['box'] = function() { return this['oauth-base'].apply(this, arguments); };
EditUriBackendConfig.builders['dropbox'] = function() { return this['oauth-base'].apply(this, arguments); };
@@ -718,7 +714,6 @@ backupApp.service('EditUriBuiltins', function(AppService, AppUtils, SystemInfo,
EditUriBackendConfig.validaters['googledrive'] = EditUriBackendConfig.validaters['authid-base'];
EditUriBackendConfig.validaters['gcs'] = EditUriBackendConfig.validaters['authid-base'];
- EditUriBackendConfig.validaters['amzcd'] = EditUriBackendConfig.validaters['authid-base'];
EditUriBackendConfig.validaters['box'] = EditUriBackendConfig.validaters['authid-base'];
EditUriBackendConfig.validaters['dropbox'] = EditUriBackendConfig.validaters['authid-base'];
EditUriBackendConfig.validaters['onedrive'] = EditUriBackendConfig.validaters['authid-base'];
diff --git a/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js b/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js
index 8ec89f270..b83792e88 100644
--- a/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js
+++ b/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js
@@ -64,7 +64,6 @@ backupApp.service('SystemInfo', function($rootScope, $timeout, $cookies, AppServ
'gcs': null,
'openstack': null,
'hubic': null,
- 'amzcd': null,
'b2': null,
'mega': null,
'box': null,
diff --git a/Duplicati/Server/webroot/ngax/templates/backends/amzcd.html b/Duplicati/Server/webroot/ngax/templates/backends/amzcd.html
deleted file mode 100644
index 4104fde5c..000000000
--- a/Duplicati/Server/webroot/ngax/templates/backends/amzcd.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
WARNING: Amazon Cloud Drive API is closing August 16th 2019
-
Do not use this destination for new backups, and migrate any existing backups away before this date
-
-
-
-
-
-
-
-
-
-
diff --git a/Installer/Docker/README.md b/Installer/Docker/README.md
index ffcf4527e..8d42e54e6 100644
--- a/Installer/Docker/README.md
+++ b/Installer/Docker/README.md
@@ -1,7 +1,7 @@
# [Duplicati](https://www.duplicati.com)
Duplicati is a free, open source, backup client that securely stores encrypted, incremental, compressed backups on cloud storage services and remote file servers. It works with:
-*Amazon S3, OneDrive, Google Drive, Rackspace Cloud Files, HubiC, Backblaze (B2), Amazon Cloud Drive (AmzCD), Swift / OpenStack, WebDAV, SSH (SFTP), FTP, and more!*
+*Amazon S3, OneDrive, Google Drive, Rackspace Cloud Files, HubiC, Backblaze (B2), Swift / OpenStack, WebDAV, SSH (SFTP), FTP, and more!*
Duplicati is licensed under LGPL and available for Windows, OSX and Linux (.NET 4.6.2+ or Mono 4.8.0+ required).
diff --git a/README.md b/README.md
index 5b4182704..3f1a6b0d2 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Removed Gitter
Duplicati is a free, open source, backup client that securely stores encrypted, incremental, compressed backups on cloud storage services and remote file servers. It works with:
- *Amazon Cloud Drive and S3, [Backblaze (B2)](https://www.backblaze.com/blog/duplicati-backups-cloud-storage/ "Duplicati with Backblaze B2 Cloud Storage"), Box, Dropbox, FTP, Google Cloud and Drive, HubiC, MEGA, Microsoft Azure and OneDrive, Rackspace Cloud Files, OpenStack Storage (Swift), Sia, SSH (SFTP), WebDAV, [and more!](https://duplicati.readthedocs.io/en/latest/01-introduction/#supported-backends)*
+ *Amazon S3, [Backblaze (B2)](https://www.backblaze.com/blog/duplicati-backups-cloud-storage/ "Duplicati with Backblaze B2 Cloud Storage"), Box, Dropbox, FTP, Google Cloud and Drive, HubiC, MEGA, Microsoft Azure and OneDrive, Rackspace Cloud Files, OpenStack Storage (Swift), Sia, SSH (SFTP), WebDAV, [and more!](https://duplicati.readthedocs.io/en/latest/01-introduction/#supported-backends)*
Duplicati is licensed under LGPL and available for Windows, OSX and Linux (.NET 4.6.2+ or Mono 4.8.0+ required).