diff options
author | Kenneth Skovhede <kenneth@hexad.dk> | 2022-06-15 00:07:24 +0300 |
---|---|---|
committer | Kenneth Skovhede <kenneth@hexad.dk> | 2022-06-15 00:07:24 +0300 |
commit | f1cdd63e3242bb85772b50ba94676951083a1b7d (patch) | |
tree | 519f28057d38cd56eec9edb67bddb50055c10bf3 | |
parent | 81cbb05a8ec5989152712172621694fa10657d59 (diff) |
Removed `IDrive` backend in favor of `IDrivee2` which is using the S3 API
-rw-r--r-- | Duplicati/Library/Backend/IDrive/Duplicati.Library.Backend.IDrive.csproj | 66 | ||||
-rw-r--r-- | Duplicati/Library/Backend/IDrive/Duplicati.snk | bin | 596 -> 0 bytes | |||
-rw-r--r-- | Duplicati/Library/Backend/IDrive/IDriveApiClient.cs | 393 | ||||
-rw-r--r-- | Duplicati/Library/Backend/IDrive/IDriveBackend.cs | 212 | ||||
-rw-r--r-- | Duplicati/Library/Backend/IDrive/Properties/AssemblyInfo.cs | 43 | ||||
-rw-r--r-- | Duplicati/Library/Backend/IDrive/Strings.cs | 18 | ||||
-rw-r--r-- | Duplicati/Library/Backend/IDrive/app.config | 10 |
7 files changed, 0 insertions, 742 deletions
diff --git a/Duplicati/Library/Backend/IDrive/Duplicati.Library.Backend.IDrive.csproj b/Duplicati/Library/Backend/IDrive/Duplicati.Library.Backend.IDrive.csproj deleted file mode 100644 index c49153699..000000000 --- a/Duplicati/Library/Backend/IDrive/Duplicati.Library.Backend.IDrive.csproj +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProjectGuid>{C16639F6-DACC-4DD9-86CD-8B937516B340}</ProjectGuid> - <OutputType>Library</OutputType> - <RootNamespace>Duplicati.Library.Backend.IDrive</RootNamespace> - <AssemblyName>Duplicati.Library.Backend.IDrive</AssemblyName> - <AssemblyOriginatorKeyFile>Duplicati.snk</AssemblyOriginatorKeyFile> - <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion> - <UseMSBuildEngine>false</UseMSBuildEngine> - <TargetFrameworkProfile /> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug</OutputPath> - <DefineConstants>DEBUG;</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> - <Optimize>true</Optimize> - <OutputPath>bin\Release</OutputPath> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> - </PropertyGroup> - <ItemGroup> - <Reference Include="System" /> - <Reference Include="System.Net.Http" /> - <Reference Include="System.Xml" /> - </ItemGroup> - <ItemGroup> - <Compile Include="IDriveApiClient.cs" /> - <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="IDriveBackend.cs" /> - <Compile Include="Strings.cs" /> - </ItemGroup> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <ItemGroup> - <ProjectReference Include="..\..\Interface\Duplicati.Library.Interface.csproj"> - <Project>{C5899F45-B0FF-483C-9D38-24A9FCAAB237}</Project> - <Name>Duplicati.Library.Interface</Name> - </ProjectReference> - <ProjectReference Include="..\..\Localization\Duplicati.Library.Localization.csproj"> - <Project>{B68F2214-951F-4F78-8488-66E1ED3F50BF}</Project> - <Name>Duplicati.Library.Localization</Name> - </ProjectReference> - <ProjectReference Include="..\..\Utility\Duplicati.Library.Utility.csproj"> - <Project>{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}</Project> - <Name>Duplicati.Library.Utility</Name> - </ProjectReference> - <ProjectReference Include="..\..\Common\Duplicati.Library.Common.csproj"> - <Project>{D63E53E4-A458-4C2F-914D-92F715F58ACF}</Project> - <Name>Duplicati.Library.Common</Name> - </ProjectReference> - </ItemGroup> - <ItemGroup> - <None Include="app.config" /> - </ItemGroup> -</Project>
\ No newline at end of file diff --git a/Duplicati/Library/Backend/IDrive/Duplicati.snk b/Duplicati/Library/Backend/IDrive/Duplicati.snk Binary files differdeleted file mode 100644 index e0c1e2dd8..000000000 --- a/Duplicati/Library/Backend/IDrive/Duplicati.snk +++ /dev/null diff --git a/Duplicati/Library/Backend/IDrive/IDriveApiClient.cs b/Duplicati/Library/Backend/IDrive/IDriveApiClient.cs deleted file mode 100644 index b37af6677..000000000 --- a/Duplicati/Library/Backend/IDrive/IDriveApiClient.cs +++ /dev/null @@ -1,393 +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 System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Reflection; -using System.Security.Authentication; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -// Full IDrive Sync API documentation can be found here: https://www.idrivesync.com/evs/web-developers-guide.htm -namespace Duplicati.Library.Backend.IDrive -{ - /// <summary> - /// Provides access to an IDrive Sync. - /// </summary> - public class IDriveApiClient - { - private const string IDRIVE_AUTH_CGI_URL = "https://www1.idrive.com/cgi-bin/v1/user-details.cgi"; - private const string IDRIVE_SYNC_GET_SERVER_ADDRESS_URL = "https://evs.idrivesync.com/evs/getServerAddress"; - private const string SUCCESS = "SUCCESS"; - private const string MESSAGE_ATTRIBUTE = "message"; - private const string XML_RESPONSE_TAG = "tree"; - - private string _idriveUsername; - private string _idrivePassword; - - private string _syncUsername; - private string _syncPassword; - private string _syncHostname; - - private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - private static SemaphoreSlim UploadSemaphore = new SemaphoreSlim(10); - - public string UserAgent { get; set; } = "Duplicati-IDrive-API-Client/" + Assembly.GetExecutingAssembly().GetName().Version; - - public IDriveApiClient() - { - } - - public async Task LoginAsync(string username, string password) - { - _idriveUsername = username; - _idrivePassword = password; - - await IDriveAuthAsync(_cancellationTokenSource.Token); - await UpdateSyncHostnameAsync(_cancellationTokenSource.Token); - } - - private async Task IDriveAuthAsync(CancellationToken cancellationToken) - { - // IDrive auth logic was reverse engineered from code found in the IDriveForLinux PERL scripts provided by IDrive. Download from: https://www.idrive.com/linux-backup-scripts - // The auth response payload contains the login credentials for the associated IDrive Sync account. - const string methodName = IDRIVE_AUTH_CGI_URL; - using (var httpClient = GetHttpClient()) - { - var parameters = new List<KeyValuePair<string, string>>() { - new KeyValuePair<string, string> ( "username", _idriveUsername), - new KeyValuePair<string, string> ( "password", _idrivePassword) - }; - var content = new FormUrlEncodedContent(parameters); - using (var response = await httpClient.PostAsync(IDRIVE_AUTH_CGI_URL, content)) - { - if (response.StatusCode != System.Net.HttpStatusCode.OK) - throw new AuthenticationException($"Failed IDrive authentication request. Server response: {response}"); - - // Sample response (masked): "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<root>\n <login remote_manage_ip=\"173.255.13.30\" quota=\"5000000000000\" datacenter=\"evsvirginia.idrive.com\" enctype=\"DEFAULT\" pns_sync=\"notify2.idrive.com\" remote_manage_server_https=\"wsn16s.idrive.com\" jspsrvr=\"www.idrive.com\" plan_type=\"Regular\" dedup=\"on\" username_sync=\"abc12345678901234def\" password_sync=\"fed09887654321098cba\" dedup_enabled=\"no\" desc=\"Success\" evssrvrip=\"148.51.142.138\" plan=\"Personal\" acctype=\"IBSYNC\" cnfgstat=\"SET\" accstat=\"Y\" evswebsrvr=\"evsweb5114.idrive.com\" remote_manage_websock_server=\"yes\" evssrvr=\"evs5114.idrive.com\" message=\"SUCCESS\" remote_manage_ip_https=\"173.255.13.31\" cnfgstatus=\"Y\" remote_manage_server=\"wsn16.idrive.com\" evswebsrvrip=\"148.51.142.139\" quota_used=\"100000000000\"></login>\n</root>\n" - string responseString = await response.Content.ReadAsStringAsync(); - var responseXml = new XmlDocument(); - responseXml.LoadXml(responseString); - var nodes = responseXml.GetElementsByTagName("login"); - if (nodes.Count == 0) - throw new AuthenticationException($"Failed '{methodName}' request. Unexpected authentication response data (no login element). Server response: {response}"); - - var responseNode = nodes[0]; - - if (responseNode.Attributes[MESSAGE_ATTRIBUTE]?.Value != SUCCESS) - throw new AuthenticationException($"Failed IDrive authentication request. Non-{SUCCESS}. Description: {responseNode.Attributes["desc"]?.Value}"); - - _syncUsername = responseNode.Attributes["username_sync"]?.Value; - _syncPassword = responseNode.Attributes["password_sync"]?.Value; - - if (string.IsNullOrEmpty(_syncUsername) || string.IsNullOrEmpty(_syncPassword)) - throw new AuthenticationException($"Failed '{methodName}' request. IDrive Sync username and/or password were not provided. Server response: {response}"); - } - } - } - - private async Task UpdateSyncHostnameAsync(CancellationToken cancellationToken) - { - // The API docs state that the sync web API server may change over time and must be retrieved on each login. - // The server may be different for different accounts, depending where the data is stored. - var responseNode = await GetSimpleTreeResponseAsync(IDRIVE_SYNC_GET_SERVER_ADDRESS_URL, "getServerAddress", cancellationToken); - - _syncHostname = responseNode.Attributes["webApiServer"]?.Value; - - if (string.IsNullOrEmpty(_syncHostname)) - throw new AuthenticationException($"Failed 'getServerAddress' request. Empty hostname. Tree XML: {responseNode.OuterXml}"); - } - - public async Task<List<FileEntry>> GetFileEntryListAsync(string directoryPath, string searchCriteria = null) - { - string methodName = string.IsNullOrEmpty(searchCriteria) ? "browseFolder" : "searchFiles"; - // NOTE: The IDrive "searchFiles" API method has a bug that returns the name of the directory being listed as one of the items when searching for "*" - string url = GetSyncServiceUrl(methodName); - var list = new List<FileEntry>(); - - using (var httpClient = GetHttpClient()) - using (var content = GetSyncPostContent(new NameValueCollection { { "p", directoryPath }, { "searchkey", searchCriteria } })) - using (var response = await httpClient.PostAsync(url, content)) - { - if (response.StatusCode != System.Net.HttpStatusCode.OK) - throw new ApplicationException($"Failed '{methodName}' request. Server response: {response}"); - - using (var responseStream = await response.Content.ReadAsStreamAsync()) - using (var xmlReader = XmlReader.Create(responseStream, new XmlReaderSettings() { Async = true })) - { - bool success = false; - while (await xmlReader.ReadAsync()) - { - if (xmlReader.Name != XML_RESPONSE_TAG) - continue; - - success = xmlReader.GetAttribute(MESSAGE_ATTRIBUTE) == SUCCESS; - break; - } - - if (!success) - throw new ApplicationException($"Failed '{methodName}' request. Non-{SUCCESS}. Description: {xmlReader.GetAttribute("desc")}"); - - while (await xmlReader.ReadAsync()) - { - // Item XML example: <item restype="1" resname="Myoffice.txt" size="9583" lmd="2010/05/26 01:58:57" ver="1" thumb="N"/> - if (xmlReader.Name != "item") - continue; - - string resname = xmlReader.GetAttribute("resname"); - if (string.IsNullOrEmpty(resname)) - continue; - - string restype = xmlReader.GetAttribute("restype"); - string size = xmlReader.GetAttribute("size"); - string lmd = xmlReader.GetAttribute("lmd"); - - long.TryParse(size, out long parsedSize); - DateTime.TryParse(lmd, out DateTime parsedModificationDate); - - var fileEntry = new FileEntry(resname) - { - IsFolder = restype != "1", - Name = resname, - Size = parsedSize, - LastModification = parsedModificationDate - }; - - list.Add(fileEntry); - } - } - } - - return list; - } - - public async Task CreateDirectoryAsync(string directoryName, string baseDirectoryPath, CancellationToken cancellationToken) - { - const string methodName = "createFolder"; - string url = GetSyncServiceUrl(methodName); - try - { - await GetSimpleTreeResponseAsync(url, methodName, cancellationToken, new NameValueCollection { { "p", baseDirectoryPath }, { "foldername", directoryName } }); - } - catch (Exception ex) - { - if (ex.Message.Contains(@"FOLDER ALREADY EXISTS WITH THIS NAME.")) - return; - - throw; - } - } - - public async Task DeleteAsync(string filePath, CancellationToken cancellationToken, bool moveToTrash = true) - { - const string methodName = "deleteFile"; - string url = GetSyncServiceUrl(methodName); - await GetSimpleTreeResponseAsync(url, methodName, cancellationToken, new NameValueCollection { { "p", filePath }, { "trash", moveToTrash ? "yes" : "no" } }); - } - - public async Task<FileEntry> UploadAsync(Stream stream, string filename, string directoryPath, CancellationToken cancellationToken) - { - const string methodName = "uploadFile"; - string url = GetSyncServiceUrl(methodName); - - await UploadSemaphore.WaitAsync(cancellationToken); - - try - { - if (cancellationToken.IsCancellationRequested) - throw new OperationCanceledException(); - - long? streamLength = null; - try { streamLength = stream.Length; } - catch { } // Fail gracefully is stream does not support Length - - using (var httpClient = GetHttpClient(TimeSpan.FromHours(24))) - using (var content = GetSyncPostContent(new NameValueCollection { { "p", directoryPath } }, isMultiPart: true)) - using (var streamContent = new StreamContent(stream)) - { - ((MultipartFormDataContent)content).Add(streamContent, filename, filename); - - using (var response = await httpClient.PostAsync(url, content, cancellationToken)) - { - if (cancellationToken.IsCancellationRequested) - throw new OperationCanceledException(); - - if (response.StatusCode != System.Net.HttpStatusCode.OK) - throw new ApplicationException($"Failed '{methodName}' request. Server response: {response}"); - - string responseString = await response.Content.ReadAsStringAsync(); - var responseXml = new XmlDocument(); - responseXml.LoadXml(responseString); - var nodes = responseXml.GetElementsByTagName(XML_RESPONSE_TAG); - if (nodes.Count == 0) - throw new ApplicationException($"Failed '{methodName}' request. Unexpected response. Server response: {response}"); - - var responseNode = nodes[0]; - if (responseNode == null) - throw new ApplicationException($"Failed '{methodName}' request. Missing {XML_RESPONSE_TAG} node. Server response: {response}"); - - if (responseNode.Attributes[MESSAGE_ATTRIBUTE]?.Value != SUCCESS) - throw new ApplicationException($"Failed '{methodName}' request. Non-{SUCCESS}. Description: {responseNode.Attributes["desc"]?.Value}"); - } - } - - // Double check the upload by searching for the file on the server and validating the response - var fileEntryList = await GetFileEntryListAsync(directoryPath, filename); - if (fileEntryList.Count != 1) - throw new FileMissingException($"Upload failed. File not found on server."); - - var fileEntry = fileEntryList[0]; - - if (streamLength != null && fileEntry.Size != streamLength.Value) - throw new FileMissingException($"Upload failed. File size on server does not match source."); - - if (fileEntry.Name != filename) - throw new FileMissingException($"Upload failed. Wrong file not found on server."); // this should never happen - - return fileEntry; - } - finally - { - UploadSemaphore.Release(); - } - } - - public async Task DownloadAsync(string filePath, Stream stream) - { - const string methodName = "downloadFile"; - string url = GetSyncServiceUrl(methodName); - - using (var httpClient = GetHttpClient()) - using (var content = GetSyncPostContent(new NameValueCollection { { "p", filePath } })) - using (var response = await httpClient.PostAsync(url, content)) - { - if (response.StatusCode != System.Net.HttpStatusCode.OK) - throw new FileMissingException($"Failed '{methodName}' request. Server response: {response}"); - - response.Headers.TryGetValues("RESTORE_STATUS", out var restoreStatus); // The download API uses RESTORE_STATUS to indicate success instead of body XML - - using (var responseStream = await response.Content.ReadAsStreamAsync()) - { - if (restoreStatus.FirstOrDefault() == SUCCESS) - { - Library.Utility.Utility.CopyStream(responseStream, stream); - return; - } - - using (var xmlReader = XmlReader.Create(responseStream, new XmlReaderSettings() { Async = true })) - { - bool success = false; - while (await xmlReader.ReadAsync()) - { - if (xmlReader.Name != XML_RESPONSE_TAG) - continue; - - success = xmlReader.GetAttribute(MESSAGE_ATTRIBUTE) == SUCCESS; - break; - } - - if (!success) - throw new FileMissingException($"Failed '{methodName}' request. Non-{SUCCESS}. Description: {xmlReader.GetAttribute("desc")}"); - - throw new FileMissingException($"Failed '{methodName}' request. Invalid RESTORE_STATUS result with invalid {SUCCESS} message."); // this should never happen - } - } - } - } - - private HttpClient GetHttpClient(TimeSpan? timeout = null) - { - var httpClient = new HttpClient() - { - Timeout = timeout ?? TimeSpan.FromMinutes(5) // more generous default timeout - }; - - if (!string.IsNullOrEmpty(UserAgent)) - httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent); - - return httpClient; - } - - private string GetSyncServiceUrl(string serviceName) - { - return $"https://{_syncHostname}/evs/{serviceName}"; - } - - private async Task<XmlNode> GetSimpleTreeResponseAsync(string url, string methodName, CancellationToken cancellationToken, NameValueCollection parameters = null) - { - using (var httpClient = GetHttpClient()) - using (var content = GetSyncPostContent(parameters)) - using (var response = await httpClient.PostAsync(url, content, cancellationToken)) - { - if (response.StatusCode != System.Net.HttpStatusCode.OK) - throw new ApplicationException($"Failed '{methodName}' request. Server response: {response}"); - - // Sample response: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tree message=\"SUCCESS\" cmdUtilityServer=\"evs19.idrivesync.com\"\n cmdUtilityServerIP=\"4.71.135.136\"\n webApiServer=\"evsweb19.idrivesync.com\" webApiServerIP=\"4.71.135.137\"\n faceWebApiServer=\"\" faceWebApiServerIP=\"\" dedup=\"off\"/>\n" - string responseString = await response.Content.ReadAsStringAsync(); - var responseXml = new XmlDocument(); - responseXml.LoadXml(responseString); - var nodes = responseXml.GetElementsByTagName(XML_RESPONSE_TAG); - if (nodes.Count == 0) - throw new ApplicationException($"Failed '{methodName}' request. Unexpected response. Server response: {response}"); - - var responseNode = nodes[0]; - if (responseNode == null) - throw new ApplicationException($"Failed '{methodName}' request. Missing {XML_RESPONSE_TAG} node. Server response: {response}"); - - if (responseNode.Attributes[MESSAGE_ATTRIBUTE]?.Value != SUCCESS) - throw new ApplicationException($"Failed '{methodName}' request. Non-{SUCCESS}. Description: {responseNode.Attributes["desc"]?.Value}"); - - return responseNode; - } - } - - private HttpContent GetSyncPostContent(NameValueCollection parameters = null, bool isMultiPart = false) - { - var allParameters = new List<KeyValuePair<string, string>>() - { - new KeyValuePair<string, string> ( "uid", _syncUsername), - new KeyValuePair<string, string> ( "pwd", _syncPassword) - }; - - if (parameters != null) - { - foreach (string key in parameters.Keys) - { - allParameters.Add(new KeyValuePair<string, string>(key, parameters[key])); - } - } - - if (!isMultiPart) - return new FormUrlEncodedContent(allParameters); - - var content = new MultipartFormDataContent(Guid.NewGuid().ToString()); - foreach (var parameter in allParameters) - { - content.Add(new StringContent(parameter.Value, Encoding.UTF8), parameter.Key); - } - - return content; - } - } -} diff --git a/Duplicati/Library/Backend/IDrive/IDriveBackend.cs b/Duplicati/Library/Backend/IDrive/IDriveBackend.cs deleted file mode 100644 index ba8561289..000000000 --- a/Duplicati/Library/Backend/IDrive/IDriveBackend.cs +++ /dev/null @@ -1,212 +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 System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace Duplicati.Library.Backend.IDrive -{ - // ReSharper disable once UnusedMember.Global - // This class is instantiated dynamically in the BackendLoader. - public class IDriveBackend : IBackend, IStreamingBackend - { - private readonly string _username = null; - private readonly string _password = null; - private readonly string _baseDirectoryPath = null; - public string DisplayName => Strings.IDriveBackend.DisplayName; - public string Description => Strings.IDriveBackend.Description; - public string[] DNSName => null; - public string ProtocolKey => "idrive"; - CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - - public IList<ICommandLineArgument> SupportedCommands - { - get - { - return new List<ICommandLineArgument>(new ICommandLineArgument[] { - new CommandLineArgument("auth-password", CommandLineArgument.ArgumentType.Password, Strings.IDriveBackend.AuthPasswordDescriptionShort, Strings.IDriveBackend.AuthPasswordDescriptionLong), - new CommandLineArgument("auth-username", CommandLineArgument.ArgumentType.String, Strings.IDriveBackend.AuthUsernameDescriptionShort, Strings.IDriveBackend.AuthUsernameDescriptionLong) - }); - } - } - - private Dictionary<string, FileEntry> _fileCache; - protected Dictionary<string, FileEntry> FileCache - { - get - { - if (_fileCache == null) - ResetFileCacheAsync().Wait(); - - return _fileCache; - } - set - { - _fileCache = value; - } - } - - private IDriveApiClient _client; - protected IDriveApiClient Client - { - get - { - if (_client == null) - { - var cl = new IDriveApiClient(); - cl.LoginAsync(_username, _password).Wait(); - _client = cl; - } - - return _client; - } - } - - public IDriveBackend() - { - } - - public IDriveBackend(string url, Dictionary<string, string> options) - { - var uri = new Utility.Uri(url); // Sample url: idrive://Directory1/SubDirectory1?auth-username=MyUsername&auth-password=MyPassword - _username = uri.Username; - _password = uri.Password; - - if (string.IsNullOrEmpty(_username) && options.TryGetValue("auth-username", out string username)) - _username = username; - if (string.IsNullOrEmpty(_password) && options.TryGetValue("auth-password", out string password)) - _password = password; - - if (string.IsNullOrEmpty(_username)) - throw new UserInformationException(Strings.IDriveBackend.NoUsernameError, "IDriveNoUsername"); - if (string.IsNullOrEmpty(_password)) - throw new UserInformationException(Strings.IDriveBackend.NoPasswordError, "IDriveNoPassword"); - - _baseDirectoryPath = ("/" + (uri.HostAndPath ?? "").Trim('/') + "/").Replace("//", "/"); - } - - private async Task ResetFileCacheAsync() - { - FileCache = (await Client.GetFileEntryListAsync(_baseDirectoryPath)) - .Where(x => !x.IsFolder) - .ToDictionary(x => x.Name, x => x); - } - - public IEnumerable<IFileEntry> List() - { - return FileCache.Values; - } - - public void Get(string filename, string localFilePath) - { - using (var fileStream = File.Create(localFilePath)) - Get(filename, fileStream); - } - - public void Get(string filename, Stream stream) - { - Client.DownloadAsync(Path.Combine(_baseDirectoryPath, filename), stream).Wait(); - } - - public async Task PutAsync(string filename, string localFilePath, CancellationToken cancellationToken) - { - using (var fileStream = File.OpenRead(localFilePath)) - await PutAsync(filename, fileStream, cancellationToken); - } - - public async Task PutAsync(string filename, Stream stream, CancellationToken cancellationToken) - { - try - { - var fileEntry = await Client.UploadAsync(stream, filename, _baseDirectoryPath, cancellationToken); - FileCache[filename] = fileEntry; - } - catch - { - FileCache = null; - throw; - } - } - - public void Delete(string filename) - { - try - { - if (!FileCache.ContainsKey(filename)) - { - ResetFileCacheAsync().Wait(); - - if (!FileCache.ContainsKey(filename)) - throw new FileMissingException(); - } - - Client.DeleteAsync(Path.Combine(_baseDirectoryPath, filename), _cancellationTokenSource.Token, false).Wait(); - - FileCache.Remove(filename); - } - catch - { - FileCache = null; - throw; - } - } - - public void CreateFolder() - { - var directoryParts = _baseDirectoryPath.Split('/').Where(d => !string.IsNullOrEmpty(d)); - string baseDirectory = "/"; - - foreach (string directoryPart in directoryParts) - { - Client.CreateDirectoryAsync(directoryPart, baseDirectory, _cancellationTokenSource.Token).Wait(); - baseDirectory += directoryPart + "/"; - } - } - - public void Test() - { - this.TestList(); - } - - #region IDisposable Support - private bool _disposedValue = false; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - } - - _disposedValue = true; - } - } - - // This code added to correctly implement the disposable pattern. - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - } - #endregion - } -} diff --git a/Duplicati/Library/Backend/IDrive/Properties/AssemblyInfo.cs b/Duplicati/Library/Backend/IDrive/Properties/AssemblyInfo.cs deleted file mode 100644 index 80c373101..000000000 --- a/Duplicati/Library/Backend/IDrive/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2022, 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.IDrive")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("Doug Krahmer")] -[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.1")] - -// 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/IDrive/Strings.cs b/Duplicati/Library/Backend/IDrive/Strings.cs deleted file mode 100644 index 9bb775e29..000000000 --- a/Duplicati/Library/Backend/IDrive/Strings.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Duplicati.Library.Localization.Short; - -namespace Duplicati.Library.Backend.Strings -{ - internal static class IDriveBackend - { - public static string DisplayName { get { return LC.L(@"IDrive"); } } - public static string AuthPasswordDescriptionLong { get { return LC.L(@"The password used to connect to the server. This may also be supplied as the environment variable ""AUTH_PASSWORD""."); } } - public static string AuthPasswordDescriptionShort { get { return LC.L(@"Supplies the password used to connect to the server"); } } - public static string AuthUsernameDescriptionLong { get { return LC.L(@"The username used to connect to the server. This may also be supplied as the environment variable ""AUTH_USERNAME""."); } } - public static string AuthUsernameDescriptionShort { get { return LC.L(@"Supplies the username used to connect to the server"); } } - public static string AuthTwoFactorKeyDescriptionShort { get { return LC.L(@"The shared secret used to generate two-factor TOTP codes."); } } - public static string AuthTwoFactorKeyDescriptionLong { get { return LC.L(@"For accounts with two-factor authentication enabled, this is the shared secret used to generate the two-factor TOTP codes."); } } - public static string NoPasswordError { get { return LC.L(@"No password given"); } } - public static string NoUsernameError { get { return LC.L(@"No username given"); } } - public static string Description { get { return LC.L(@"This backend can read and write data to IDrive Sync. Allowed formats are: ""idrive://folder/subfolder"""); } } - } -}
\ No newline at end of file diff --git a/Duplicati/Library/Backend/IDrive/app.config b/Duplicati/Library/Backend/IDrive/app.config deleted file mode 100644 index ad53561b2..000000000 --- a/Duplicati/Library/Backend/IDrive/app.config +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<configuration> - <startup> - <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" /> - </startup> - <runtime> - <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> - </assemblyBinding> - </runtime> -</configuration> |