2024-02-28 15:45:30 +01:00
|
|
|
// Copyright (C) 2024, The Duplicati Team
|
|
|
|
|
// https://duplicati.com, hello@duplicati.com
|
|
|
|
|
//
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
// to deal in the Software without restriction, including without limitation
|
|
|
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
//
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
//
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
2010-06-19 21:08:21 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using Duplicati.Library.Interface;
|
2013-05-02 21:12:46 +02:00
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
using System.Web;
|
2013-05-04 14:23:10 +02:00
|
|
|
using System.Linq;
|
2019-03-21 19:26:14 -07:00
|
|
|
using System.Runtime.ExceptionServices;
|
2010-06-19 21:08:21 +00:00
|
|
|
|
|
|
|
|
namespace Duplicati.Library.DynamicLoader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads all backends dynamically and exposes a list of those
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class BackendLoader
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Implementation overrides specific to backends
|
|
|
|
|
/// </summary>
|
|
|
|
|
private class BackendLoaderSub : DynamicLoader<IBackend>
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the protocol key
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="item">The item to load the key for</param>
|
|
|
|
|
/// <returns>The protocol key</returns>
|
|
|
|
|
protected override string GetInterfaceKey(IBackend item)
|
|
|
|
|
{
|
|
|
|
|
return item.ProtocolKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the subfolders searched for backends
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected override string[] Subfolders
|
|
|
|
|
{
|
|
|
|
|
get { return new string[] {"backends"}; }
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-05 13:09:55 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Instanciates a specific backend, given the url and options
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="url">The url to create the instance for</param>
|
|
|
|
|
/// <param name="options">The options to pass to the instance constructor</param>
|
|
|
|
|
/// <returns>The instanciated backend or null if the url is not supported</returns>
|
|
|
|
|
public IBackend GetBackend(string url, Dictionary<string, string> options)
|
|
|
|
|
{
|
2013-05-05 23:46:00 +02:00
|
|
|
var uri = new Utility.Uri(url);
|
2013-05-05 13:09:55 +02:00
|
|
|
|
2010-06-19 21:08:21 +00:00
|
|
|
LoadInterfaces();
|
2013-05-02 21:12:46 +02:00
|
|
|
|
|
|
|
|
var newOpts = new Dictionary<string, string>(options);
|
2014-07-15 13:33:32 +02:00
|
|
|
foreach (var key in uri.QueryParameters.AllKeys)
|
2013-05-05 23:46:00 +02:00
|
|
|
newOpts[key] = uri.QueryParameters[key];
|
2010-06-19 21:08:21 +00:00
|
|
|
|
|
|
|
|
lock (m_lock)
|
|
|
|
|
{
|
2014-07-15 13:33:32 +02:00
|
|
|
try
|
2013-05-04 14:23:10 +02:00
|
|
|
{
|
2014-07-15 13:33:32 +02:00
|
|
|
if (m_interfaces.ContainsKey(uri.Scheme))
|
|
|
|
|
return (IBackend)Activator.CreateInstance(m_interfaces[uri.Scheme].GetType(), url, newOpts);
|
2017-11-26 10:53:14 -08:00
|
|
|
else if (uri.Scheme.EndsWith("s", StringComparison.Ordinal))
|
2013-05-04 14:23:10 +02:00
|
|
|
{
|
2014-07-15 13:33:32 +02:00
|
|
|
var tmpscheme = uri.Scheme.Substring(0, uri.Scheme.Length - 1);
|
|
|
|
|
if (m_interfaces.ContainsKey(tmpscheme))
|
|
|
|
|
{
|
|
|
|
|
var commands = m_interfaces[tmpscheme].SupportedCommands;
|
2018-10-06 15:50:05 -07:00
|
|
|
if (commands != null && (commands.Any(x =>
|
2017-09-18 23:23:45 -06:00
|
|
|
x.Name.Equals("use-ssl", StringComparison.OrdinalIgnoreCase) ||
|
2018-10-06 15:50:05 -07:00
|
|
|
(x.Aliases != null && x.Aliases.Any(y => y.Equals("use-ssl", StringComparison.OrdinalIgnoreCase)))
|
|
|
|
|
)))
|
2014-07-15 13:33:32 +02:00
|
|
|
{
|
|
|
|
|
newOpts["use-ssl"] = "true";
|
|
|
|
|
return (IBackend)Activator.CreateInstance(m_interfaces[tmpscheme].GetType(), url, newOpts);
|
|
|
|
|
}
|
2013-05-04 14:23:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-15 13:33:32 +02:00
|
|
|
catch (System.Reflection.TargetInvocationException tex)
|
|
|
|
|
{
|
|
|
|
|
if (tex.InnerException != null)
|
2019-03-21 19:26:14 -07:00
|
|
|
{
|
|
|
|
|
// Unwrap exceptions for nicer display. The ExceptionDispatchInfo class allows us to
|
|
|
|
|
// rethrow an exception without changing the stack trace.
|
|
|
|
|
ExceptionDispatchInfo.Capture(tex.InnerException).Throw();
|
|
|
|
|
}
|
2014-07-15 13:33:32 +02:00
|
|
|
|
|
|
|
|
throw;
|
|
|
|
|
}
|
2013-05-04 14:23:10 +02:00
|
|
|
|
|
|
|
|
return null;
|
2010-06-19 21:08:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2013-05-05 13:09:55 +02:00
|
|
|
/// Gets the supported commands for a certain url
|
2010-06-19 21:08:21 +00:00
|
|
|
/// </summary>
|
2013-05-05 13:09:55 +02:00
|
|
|
/// <param name="url">The url to find commands for</param>
|
|
|
|
|
/// <returns>The supported commands or null if the url scheme was not supported</returns>
|
|
|
|
|
public IList<ICommandLineArgument> GetSupportedCommands(string url)
|
2010-06-19 21:08:21 +00:00
|
|
|
{
|
2013-05-05 23:46:00 +02:00
|
|
|
var uri = new Utility.Uri(url);
|
2010-06-19 21:08:21 +00:00
|
|
|
|
|
|
|
|
LoadInterfaces();
|
|
|
|
|
|
|
|
|
|
lock (m_lock)
|
|
|
|
|
{
|
|
|
|
|
IBackend b;
|
2013-05-05 23:46:00 +02:00
|
|
|
if (m_interfaces.TryGetValue(uri.Scheme, out b) && b != null)
|
2010-06-19 21:08:21 +00:00
|
|
|
return b.SupportedCommands;
|
2017-11-26 10:53:14 -08:00
|
|
|
else if (uri.Scheme.EndsWith("s", StringComparison.Ordinal))
|
2013-05-05 13:09:55 +02:00
|
|
|
{
|
2013-05-05 23:46:00 +02:00
|
|
|
var tmpscheme = uri.Scheme.Substring(0, uri.Scheme.Length - 1);
|
2013-05-05 13:09:55 +02:00
|
|
|
if (m_interfaces.ContainsKey(tmpscheme))
|
|
|
|
|
return m_interfaces[tmpscheme].SupportedCommands;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
2010-06-19 21:08:21 +00:00
|
|
|
}
|
2013-05-05 23:46:00 +02:00
|
|
|
}
|
2010-06-19 21:08:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The static instance used to access backend information
|
|
|
|
|
/// </summary>
|
2018-05-23 21:18:01 -07:00
|
|
|
private static readonly BackendLoaderSub _backendLoader = new BackendLoaderSub();
|
2010-06-19 21:08:21 +00:00
|
|
|
|
|
|
|
|
#region Public static API
|
2018-05-23 21:18:01 -07:00
|
|
|
|
2010-06-19 21:08:21 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a list of loaded backends, the instances can be used to extract interface information, not used to interact with the backend.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IBackend[] Backends { get { return _backendLoader.Interfaces; } }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a list of keys supported
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static string[] Keys { get { return _backendLoader.Keys; } }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the supported commands for a given backend
|
|
|
|
|
/// </summary>
|
2013-05-05 13:09:55 +02:00
|
|
|
/// <param name="url">The url to find the commands for</param>
|
|
|
|
|
/// <returns>The supported commands or null if the url is not supported</returns>
|
|
|
|
|
public static IList<ICommandLineArgument> GetSupportedCommands(string url)
|
2010-06-19 21:08:21 +00:00
|
|
|
{
|
2013-05-05 13:09:55 +02:00
|
|
|
if (string.IsNullOrEmpty(url))
|
2018-03-07 17:53:08 -08:00
|
|
|
throw new ArgumentNullException(nameof(url));
|
2011-06-30 07:26:07 +00:00
|
|
|
|
2013-05-05 13:09:55 +02:00
|
|
|
return _backendLoader.GetSupportedCommands(url);
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-19 21:08:21 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Instanciates a specific backend, given the url and options
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="url">The url to create the instance for</param>
|
|
|
|
|
/// <param name="options">The options to pass to the instance constructor</param>
|
|
|
|
|
/// <returns>The instanciated backend or null if the url is not supported</returns>
|
|
|
|
|
public static IBackend GetBackend(string url, Dictionary<string, string> options)
|
|
|
|
|
{
|
|
|
|
|
return _backendLoader.GetBackend(url, options);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|