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.
2019-09-17 10:59:47 +02:00
using Duplicati.Library.Common.IO ;
2019-02-25 16:13:57 +01:00
using Duplicati.Library.Interface ;
2009-01-20 22:54:53 +00:00
using System ;
using System.Collections.Generic ;
2019-02-23 09:21:16 -06:00
using System.IO ;
2019-02-25 16:13:57 +01:00
using System.Linq ;
2009-01-20 22:54:53 +00:00
using System.Text ;
using System.Text.RegularExpressions ;
2019-02-22 21:58:40 -06:00
using System.Threading ;
using System.Threading.Tasks ;
2019-02-25 16:13:57 +01:00
2009-01-20 22:54:53 +00:00
namespace Duplicati.Library.Backend
{
2014-08-21 20:59:21 +02:00
public class S3 : IBackend , IStreamingBackend , IRenameEnabledBackend
2009-01-20 22:54:53 +00:00
{
2019-10-19 10:56:21 -07:00
private static readonly string LOGTAG = Logging . Log . LogTagFromType < S3 >();
2018-06-20 10:54:37 +02:00
2019-11-25 23:46:50 +01:00
private const string RRS_OPTION = "s3-use-rrs" ;
private const string STORAGECLASS_OPTION = "s3-storage-class" ;
private const string EU_BUCKETS_OPTION = "s3-european-buckets" ;
private const string SERVER_NAME = "s3-server-name" ;
private const string LOCATION_OPTION = "s3-location-constraint" ;
private const string SSL_OPTION = "use-ssl" ;
private const string S3_CLIENT_OPTION = "s3-client" ;
2023-09-25 02:02:55 +02:00
private const string S3_DISABLE_CHUNK_ENCODING_OPTION = "s3-disable-chunk-encoding" ;
2011-06-23 21:37:14 +00:00
2019-09-17 11:16:09 +02:00
public static readonly Dictionary < string , string > KNOWN_S3_PROVIDERS = new Dictionary < string , string >( StringComparer . OrdinalIgnoreCase ) {
{ "Amazon S3" , "s3.amazonaws.com" },
2020-05-06 19:05:22 +02:00
{ "MyCloudyPlace (EU)" , "s3.mycloudyplace.com" },
2023-03-15 16:27:58 +01:00
{ "Impossible Cloud (US)" , "us-west-1.storage.impossibleapi.net" },
2023-04-12 02:31:15 +05:30
{ "Scaleway (Amsterdam, The Netherlands)" , "s3.nl-ams.scw.cloud" },
{ "Scaleway (Paris, France)" , "s3.fr-par.scw.cloud" },
{ "Scaleway (Warsaw, Poland)" , "s3.pl-waw.scw.cloud" },
2019-09-17 11:16:09 +02:00
{ "Hosteurope" , "cs.hosteurope.de" },
{ "Dunkel" , "dcs.dunkel.de" },
{ "DreamHost" , "objects.dreamhost.com" },
{ "dinCloud - Chicago" , "d3-ord.dincloud.com" },
{ "dinCloud - Los Angeles" , "d3-lax.dincloud.com" },
2021-09-19 15:11:58 +02:00
{ "Poli Systems (CH)" , "s3.polisystems.ch" },
2019-09-17 11:16:09 +02:00
{ "IBM COS (S3) Public US" , "s3-api.us-geo.objectstorage.softlayer.net" },
2020-09-10 16:44:31 -07:00
{ "Storadera" , "eu-east-1.s3.storadera.com" },
2019-09-17 11:16:09 +02:00
{ "Wasabi Hot Storage" , "s3.wasabisys.com" },
{ "Wasabi Hot Storage (US West)" , "s3.us-west-1.wasabisys.com" },
{ "Wasabi Hot Storage (EU Central)" , "s3.eu-central-1.wasabisys.com" },
2022-06-14 16:17:16 +02:00
{ "Infomaniak Swiss Backup cluster 1" , "s3.swiss-backup.infomaniak.com" },
2022-06-14 16:34:36 +02:00
{ "Infomaniak Swiss Backup cluster 2" , "s3.swiss-backup02.infomaniak.com" },
2022-06-14 16:20:29 +02:00
{ "Infomaniak Swiss Backup cluster 3" , "s3.swiss-backup03.infomaniak.com" },
{ "Infomaniak Public Cloud 1" , "s3.pub1.infomaniak.cloud" },
2011-06-23 21:37:14 +00:00
};
2012-08-20 07:55:12 +00:00
//Updated list: http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region
2019-09-17 11:16:09 +02:00
public static readonly Dictionary < string , string > KNOWN_S3_LOCATIONS = new Dictionary < string , string >( StringComparer . OrdinalIgnoreCase ){
{ "(default)" , "" },
{ "Europe (EU)" , "EU" },
{ "Europe (EU, Frankfurt)" , "eu-central-1" },
{ "Europe (EU, Ireland)" , "eu-west-1" },
{ "Europe (EU, London)" , "eu-west-2" },
{ "Europe (EU, Paris)" , "eu-west-3" },
{ "Europe (EU, Stockholm)" , "eu-north-1" },
2020-06-14 11:23:45 -07:00
{ "Europe (EU, Milan)" , "eu-south-1" },
2019-09-17 11:16:09 +02:00
{ "US East (Northern Virginia)" , "us-east-1" },
{ "US East (Ohio)" , "us-east-2" },
{ "US West (Northern California)" , "us-west-1" },
{ "US West (Oregon)" , "us-west-2" },
{ "Canada (Central)" , "ca-central-1" },
{ "Asia Pacific (Hong Kong)" , "ap-east-1" },
{ "Asia Pacific (Mumbai)" , "ap-south-1" },
{ "Asia Pacific (Singapore)" , "ap-southeast-1" },
{ "Asia Pacific (Sydney)" , "ap-southeast-2" },
{ "Asia Pacific (Tokyo)" , "ap-northeast-1" },
{ "Asia Pacific (Seoul)" , "ap-northeast-2" },
{ "Asia Pacific (Osaka-Local)" , "ap-northeast-3" },
{ "South America (São Paulo)" , "sa-east-1" },
{ "China (Beijing)" , "cn-north-1" },
{ "China (Ningxia)" , "cn-northwest-1" },
{ "Middle East (Bahrain)" , "me-south-1" },
2011-06-23 21:37:14 +00:00
};
2013-03-08 23:44:03 +01:00
2019-09-17 11:16:09 +02:00
public static readonly Dictionary < string , string > DEFAULT_S3_LOCATION_BASED_HOSTS = new Dictionary < string , string >( StringComparer . OrdinalIgnoreCase ){
2019-09-17 11:17:58 +02:00
{ "EU" , "s3.eu-west-1.amazonaws.com" },
{ "ca-central-1" , "s3.ca-central-1.amazonaws.com" },
{ "eu-west-1" , "s3.eu-west-1.amazonaws.com" },
{ "eu-west-2" , "s3.eu-west-2.amazonaws.com" },
{ "eu-west-3" , "s3.eu-west-3.amazonaws.com" },
{ "eu-north-1" , "s3.eu-north-1.amazonaws.com" },
2020-06-14 11:23:45 -07:00
{ "eu-south-1" , "s3.eu-south-1.amazonaws.com" },
2019-09-17 11:17:58 +02:00
{ "eu-central-1" , "s3.eu-central-1.amazonaws.com" },
2019-09-17 11:16:09 +02:00
{ "us-east-1" , "s3.amazonaws.com" },
{ "us-east-2" , "s3.us-east-2.amazonaws.com" },
2019-09-17 11:17:58 +02:00
{ "us-west-1" , "s3.us-west-1.amazonaws.com" },
{ "us-west-2" , "s3.us-west-2.amazonaws.com" },
{ "ap-east-1" , "s3.ap-east-1.amazonaws.com" },
{ "ap-south-1" , "s3.ap-south-1.amazonaws.com" },
{ "ap-southeast-1" , "s3.ap-southeast-1.amazonaws.com" },
{ "ap-southeast-2" , "s3.ap-southeast-2.amazonaws.com" },
{ "ap-northeast-1" , "s3.ap-northeast-1.amazonaws.com" },
{ "ap-northeast-2" , "s3.ap-northeast-2.amazonaws.com" },
{ "ap-northeast-3" , "s3.ap-northeast-3.amazonaws.com" },
{ "sa-east-1" , "s3.sa-east-1.amazonaws.com" },
2019-09-17 11:16:09 +02:00
{ "cn-north-1" , "s3.cn-north-1.amazonaws.com.cn" },
{ "cn-northwest-1" , "s3.cn-northwest-1.amazonaws.com.cn" },
2020-06-27 12:21:59 -07:00
{ "me-south-1" , "s3.me-south-1.amazonaws.com" },
2013-03-08 23:44:03 +01:00
};
2019-09-17 11:16:09 +02:00
public static readonly Dictionary < string , string > KNOWN_S3_STORAGE_CLASSES ;
2015-11-18 20:38:27 +01:00
2019-02-25 16:13:57 +01:00
static S3 ()
{
2019-09-17 11:16:09 +02:00
var ns = new Dictionary < string , string >( StringComparer . OrdinalIgnoreCase ) {
{ "(default)" , "" },
{ "Standard" , "STANDARD" },
{ "Infrequent Access (IA)" , "STANDARD_IA" },
{ "One Zone Infrequent Access (One Zone IA)" , "ONEZONE_IA" },
{ "Glacier" , "GLACIER" },
{ "Deep Archive" , "DEEP_ARCHIVE" },
{ "Reduced Redundancy Storage (RRS)" , "REDUCED_REDUNDANCY" },
2015-11-18 20:38:27 +01:00
};
try
{
2019-02-25 16:13:57 +01:00
foreach ( var sc in ReadStorageClasses ())
2019-09-17 11:16:09 +02:00
if (! ns . Select ( x => x . Value ). Contains ( sc . Value , StringComparer . OrdinalIgnoreCase ))
ns . Add ( sc . Key , sc . Value );
2015-11-18 20:38:27 +01:00
}
catch
{
}
2019-09-17 11:16:09 +02:00
KNOWN_S3_STORAGE_CLASSES = ns ;
2015-11-18 20:38:27 +01:00
}
/// <summary>
/// Fetch storage classes from the API through reflection so we are always updated
/// </summary>
/// <returns>The storage classes.</returns>
private static IEnumerable < KeyValuePair < string , string >> ReadStorageClasses ()
{
2019-02-25 16:13:57 +01:00
foreach ( var f in typeof ( Amazon . S3 . S3StorageClass ). GetFields ( System . Reflection . BindingFlags . Static | System . Reflection . BindingFlags . DeclaredOnly | System . Reflection . BindingFlags . Public ))
2015-11-18 20:38:27 +01:00
{
if ( f . FieldType == typeof ( Amazon . S3 . S3StorageClass ))
{
var name = new Regex ( "([a-z])([A-Z])" ). Replace ( f . Name , "$1 $2" );
var prop = f . GetValue ( null ) as Amazon . S3 . S3StorageClass ;
if ( prop != null && prop . Value != null )
yield return new KeyValuePair < string , string >( name , prop . Value );
}
}
}
2018-05-23 21:18:01 -07:00
private readonly string m_bucket ;
private readonly string m_prefix ;
2009-01-20 22:54:53 +00:00
2019-11-26 22:19:11 +01:00
private const string DEFAULT_S3_HOST = "s3.amazonaws.com" ;
private const string S3_EU_REGION_NAME = "eu-west-1" ;
private const string S3_RRS_CLASS_NAME = "REDUCED_REDUNDANCY" ;
2020-05-06 19:05:22 +02:00
2019-11-22 22:37:23 +01:00
private IS3Client s3Client ;
2020-05-06 19:05:22 +02:00
2009-01-20 22:54:53 +00:00
public S3 ()
{
}
public S3 ( string url , Dictionary < string , string > options )
{
2013-05-05 23:46:00 +02:00
var uri = new Utility . Uri ( url );
uri . RequireHost ();
2019-02-25 16:13:57 +01:00
2013-05-05 23:46:00 +02:00
string host = uri . Host ;
m_prefix = uri . Path ;
2009-01-20 22:54:53 +00:00
2011-09-24 11:39:50 +00:00
string awsID = null ;
string awsKey = null ;
2013-05-05 16:40:37 +02:00
if ( options . ContainsKey ( "auth-username" ))
awsID = options [ "auth-username" ];
if ( options . ContainsKey ( "auth-password" ))
awsKey = options [ "auth-password" ];
2020-05-06 19:05:22 +02:00
2021-05-15 14:06:39 -07:00
if ( options . ContainsKey ( "aws_access_key_id" ))
awsID = options [ "aws_access_key_id" ];
if ( options . ContainsKey ( "aws-access-key-id" ))
awsID = options [ "aws-access-key-id" ];
if ( options . ContainsKey ( "aws_secret_access_key" ))
awsKey = options [ "aws_secret_access_key" ];
if ( options . ContainsKey ( "aws-secret-access-key" ))
awsKey = options [ "aws-secret-access-key" ];
2013-05-05 23:46:00 +02:00
if (! string . IsNullOrEmpty ( uri . Username ))
awsID = uri . Username ;
if (! string . IsNullOrEmpty ( uri . Password ))
awsKey = uri . Password ;
if ( string . IsNullOrEmpty ( awsID ))
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( Strings . S3Backend . NoAMZUserIDError , "S3NoAmzUserID" );
2013-05-05 23:46:00 +02:00
if ( string . IsNullOrEmpty ( awsKey ))
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( Strings . S3Backend . NoAMZKeyError , "S3NoAmzKey" );
2009-01-20 22:54:53 +00:00
2011-06-23 21:37:14 +00:00
bool euBuckets = Utility . Utility . ParseBoolOption ( options , EU_BUCKETS_OPTION );
2011-09-24 11:39:50 +00:00
bool useRRS = Utility . Utility . ParseBoolOption ( options , RRS_OPTION );
bool useSSL = Utility . Utility . ParseBoolOption ( options , SSL_OPTION );
2010-06-19 23:29:47 +00:00
2011-09-24 11:39:50 +00:00
string locationConstraint ;
options . TryGetValue ( LOCATION_OPTION , out locationConstraint );
2009-01-20 22:54:53 +00:00
2011-09-24 11:39:50 +00:00
if (! string . IsNullOrEmpty ( locationConstraint ) && euBuckets )
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( Strings . S3Backend . OptionsAreMutuallyExclusiveError ( LOCATION_OPTION , EU_BUCKETS_OPTION ), "S3CannotMixLocationAndEuOptions" );
2010-05-13 18:21:42 +00:00
2011-06-23 21:37:14 +00:00
if ( euBuckets )
2011-09-24 11:39:50 +00:00
locationConstraint = S3_EU_REGION_NAME ;
2009-01-20 22:54:53 +00:00
2015-11-18 20:38:27 +01:00
string storageClass ;
options . TryGetValue ( STORAGECLASS_OPTION , out storageClass );
if ( string . IsNullOrWhiteSpace ( storageClass ) && useRRS )
storageClass = S3_RRS_CLASS_NAME ;
2013-03-08 23:44:03 +01:00
string s3host ;
options . TryGetValue ( SERVER_NAME , out s3host );
2019-02-25 16:13:57 +01:00
if ( string . IsNullOrEmpty ( s3host ))
2013-03-08 23:44:03 +01:00
{
s3host = DEFAULT_S3_HOST ;
//Change in S3, now requires that you use location specific endpoint
if (! string . IsNullOrEmpty ( locationConstraint ))
2019-09-17 22:01:33 +02:00
{
if ( DEFAULT_S3_LOCATION_BASED_HOSTS . TryGetValue ( locationConstraint , out var s3hostmatch ))
s3host = s3hostmatch ;
}
2013-03-08 23:44:03 +01:00
}
2011-06-23 21:37:14 +00:00
//Fallback to previous formats
2011-09-24 11:39:50 +00:00
if ( host . Contains ( DEFAULT_S3_HOST ))
2009-01-20 22:54:53 +00:00
{
2011-06-23 21:37:14 +00:00
Uri u = new Uri ( url );
2011-09-24 11:39:50 +00:00
host = u . Host ;
2011-06-23 21:37:14 +00:00
m_prefix = "" ;
2009-01-20 22:54:53 +00:00
2018-09-19 14:29:40 -07:00
if ( String . Equals ( host , s3host , StringComparison . OrdinalIgnoreCase ))
2009-01-20 22:54:53 +00:00
{
2019-01-06 18:41:57 +01:00
m_bucket = Utility . Uri . UrlDecode ( u . PathAndQuery );
2011-06-23 21:37:14 +00:00
2017-11-26 10:53:14 -08:00
if ( m_bucket . StartsWith ( "/" , StringComparison . Ordinal ))
2011-06-23 21:37:14 +00:00
m_bucket = m_bucket . Substring ( 1 );
if ( m_bucket . Contains ( "/" ))
{
2017-11-26 10:53:14 -08:00
m_prefix = m_bucket . Substring ( m_bucket . IndexOf ( "/" , StringComparison . Ordinal ) + 1 );
m_bucket = m_bucket . Substring ( 0 , m_bucket . IndexOf ( "/" , StringComparison . Ordinal ));
2011-06-23 21:37:14 +00:00
}
2009-01-20 22:54:53 +00:00
}
else
2011-06-23 21:37:14 +00:00
{
//Subdomain type lookup
2018-09-19 14:29:40 -07:00
if ( host . EndsWith ( "." + s3host , StringComparison . OrdinalIgnoreCase ))
2011-06-23 21:37:14 +00:00
{
2011-09-24 11:39:50 +00:00
m_bucket = host . Substring ( 0 , host . Length - ( "." + s3host ). Length );
host = s3host ;
2019-01-06 18:41:57 +01:00
m_prefix = Utility . Uri . UrlDecode ( u . PathAndQuery );
2011-06-23 21:37:14 +00:00
2017-11-26 10:53:14 -08:00
if ( m_prefix . StartsWith ( "/" , StringComparison . Ordinal ))
2011-06-23 21:37:14 +00:00
m_prefix = m_prefix . Substring ( 1 );
}
else
2018-03-12 14:07:11 +01:00
throw new UserInformationException ( Strings . S3Backend . UnableToDecodeBucketnameError ( url ), "S3CannotDecodeBucketName" );
2011-06-23 21:37:14 +00:00
}
2009-01-20 22:54:53 +00:00
2019-02-25 16:13:57 +01:00
Logging . Log . WriteWarningMessage ( LOGTAG , "DeprecatedS3Format" , null , Strings . S3Backend . DeprecatedUrlFormat ( "s3://" + m_bucket + "/" + m_prefix ));
2011-06-23 21:37:14 +00:00
}
else
2010-06-19 23:29:47 +00:00
{
2011-06-23 21:37:14 +00:00
//The new simplified url style s3://bucket/prefix
2011-09-24 11:39:50 +00:00
m_bucket = host ;
host = s3host ;
2010-06-19 23:29:47 +00:00
}
2009-01-20 22:54:53 +00:00
m_prefix = m_prefix . Trim ();
2018-09-21 16:26:30 -07:00
if ( m_prefix . Length != 0 )
{
2018-10-27 12:17:07 +02:00
m_prefix = Util . AppendDirSeparator ( m_prefix , "/" );
2018-09-21 16:26:30 -07:00
}
2011-09-24 11:39:50 +00:00
2017-09-18 23:23:45 -06:00
// Auto-disable dns lookup for non AWS configurations
2017-08-29 10:35:41 +02:00
var hasForcePathStyle = options . ContainsKey ( "s3-ext-forcepathstyle" );
if (! hasForcePathStyle && ! DEFAULT_S3_LOCATION_BASED_HOSTS . Any ( x => string . Equals ( x . Value , host , StringComparison . OrdinalIgnoreCase )) && ! string . Equals ( host , "s3.amazonaws.com" , StringComparison . OrdinalIgnoreCase ))
options [ "s3-ext-forcepathstyle" ] = "true" ;
2017-09-18 23:23:45 -06:00
2023-09-25 02:02:55 +02:00
bool disableChunkEncoding = Utility . Utility . ParseBoolOption ( options , S3_DISABLE_CHUNK_ENCODING_OPTION );
2019-11-25 23:46:50 +01:00
options . TryGetValue ( S3_CLIENT_OPTION , out var s3ClientOptionValue );
if ( s3ClientOptionValue == "aws" || s3ClientOptionValue == null )
2019-11-21 22:37:32 +01:00
{
2023-09-25 02:02:55 +02:00
s3Client = new S3AwsClient ( awsID , awsKey , locationConstraint , host , storageClass , useSSL , disableChunkEncoding , options );
2019-11-21 22:37:32 +01:00
}
else
{
s3Client = new S3MinioClient ( awsID , awsKey , locationConstraint , host , storageClass , useSSL , options );
}
2009-01-20 22:54:53 +00:00
}
2010-10-23 11:40:43 +00:00
public static bool IsValidHostname ( string bucketname )
{
2019-11-22 22:37:23 +01:00
return ! string . IsNullOrEmpty ( bucketname ) && Amazon . S3 . Util . AmazonS3Util . ValidateV2Bucket ( bucketname );
2010-10-23 11:40:43 +00:00
}
2009-01-20 22:54:53 +00:00
2010-10-23 11:40:43 +00:00
#region IBackend Members
public string DisplayName
{
get { return Strings . S3Backend . DisplayName ; }
}
public string ProtocolKey
{
get { return "s3" ; }
}
public bool SupportsStreaming
{
get { return true ; }
}
2020-05-06 19:05:22 +02:00
2017-09-25 23:17:45 -06:00
public IEnumerable < IFileEntry > List ()
{
foreach ( IFileEntry file in Connection . ListBucket ( m_bucket , m_prefix ))
{
(( FileEntry ) file ). Name = file . Name . Substring ( m_prefix . Length );
//Fix for a bug in Duplicati 1.0 beta 3 and earlier, where filenames are incorrectly prefixed with a slash
2017-11-26 10:53:14 -08:00
if ( file . Name . StartsWith ( "/" , StringComparison . Ordinal ) && ! m_prefix . StartsWith ( "/" , StringComparison . Ordinal ))
2017-09-25 23:17:45 -06:00
(( FileEntry ) file ). Name = file . Name . Substring ( 1 );
yield return file ;
}
}
2021-06-12 11:30:08 -07:00
public async Task PutAsync ( string remotename , string localname , CancellationToken cancelToken )
2009-01-25 19:33:36 +00:00
{
2019-02-23 09:21:16 -06:00
using ( FileStream fs = File . Open ( localname , FileMode . Open , FileAccess . Read , FileShare . Read ))
2021-06-12 11:30:08 -07:00
await PutAsync ( remotename , fs , cancelToken );
2009-01-25 19:33:36 +00:00
}
2019-03-17 18:20:14 -05:00
public async Task PutAsync ( string remotename , Stream input , CancellationToken cancelToken )
2009-01-20 22:54:53 +00:00
{
2019-11-26 21:59:53 +01:00
await Connection . AddFileStreamAsync ( m_bucket , GetFullKey ( remotename ), input , cancelToken );
2009-01-20 22:54:53 +00:00
}
public void Get ( string remotename , string localname )
2009-01-25 19:33:36 +00:00
{
2019-11-26 21:59:53 +01:00
using ( var fs = System . IO . File . Open ( localname , System . IO . FileMode . Create , System . IO . FileAccess . Write , System . IO . FileShare . None ))
2009-01-25 19:33:36 +00:00
Get ( remotename , fs );
}
public void Get ( string remotename , System . IO . Stream output )
2009-01-20 22:54:53 +00:00
{
2020-07-08 07:52:04 -05:00
Connection . GetFileStream ( m_bucket , GetFullKey ( remotename ), output );
2009-01-20 22:54:53 +00:00
}
public void Delete ( string remotename )
{
2011-09-27 05:12:22 +00:00
Connection . DeleteObject ( m_bucket , GetFullKey ( remotename ));
2009-01-20 22:54:53 +00:00
}
2009-03-21 12:46:54 +00:00
public IList < ICommandLineArgument > SupportedCommands
{
get
{
2011-06-23 21:37:14 +00:00
StringBuilder hostnames = new StringBuilder ();
StringBuilder locations = new StringBuilder ();
2019-09-17 11:16:09 +02:00
foreach ( var s in KNOWN_S3_PROVIDERS )
2011-06-23 21:37:14 +00:00
hostnames . AppendLine ( string . Format ( "{0}: {1}" , s . Key , s . Value ));
2019-09-17 11:16:09 +02:00
foreach ( var s in KNOWN_S3_LOCATIONS )
2011-06-23 21:37:14 +00:00
locations . AppendLine ( string . Format ( "{0}: {1}" , s . Key , s . Value ));
2021-06-10 10:20:52 -07:00
var defaults = S3AwsClient . GetDefaultAmazonS3Config ();
2016-02-15 13:36:10 +01:00
2019-02-25 16:13:57 +01:00
var exts =
2016-02-15 13:36:10 +01:00
typeof ( Amazon . S3 . AmazonS3Config ). GetProperties (). Where ( x => x . CanRead && x . CanWrite && ( x . PropertyType == typeof ( string ) || x . PropertyType == typeof ( bool ) || x . PropertyType == typeof ( int ) || x . PropertyType == typeof ( long ) || x . PropertyType . IsEnum ))
. Select ( x => ( ICommandLineArgument ) new CommandLineArgument (
2019-02-25 16:13:57 +01:00
"s3-ext-" + x . Name . ToLowerInvariant (),
2016-02-15 13:36:10 +01:00
x . PropertyType == typeof ( bool ) ? CommandLineArgument . ArgumentType . Boolean : x . PropertyType . IsEnum ? CommandLineArgument . ArgumentType . Enumeration : CommandLineArgument . ArgumentType . String ,
x . Name ,
string . Format ( "Extended option {0}" , x . Name ),
string . Format ( "{0}" , x . GetValue ( defaults )),
null ,
x . PropertyType . IsEnum ? Enum . GetNames ( x . PropertyType ) : null ));
var normal = new ICommandLineArgument [] {
2021-03-07 15:38:13 -08:00
new CommandLineArgument ( "aws_secret_access_key" , CommandLineArgument . ArgumentType . Password , Strings . S3Backend . AMZKeyDescriptionShort , Strings . S3Backend . AMZKeyDescriptionLong , null , null , null , "This is deprecated, use aws-secret-access-key instead" ),
2021-02-16 10:41:55 +00:00
new CommandLineArgument ( "aws-secret-access-key" , CommandLineArgument . ArgumentType . Password , Strings . S3Backend . AMZKeyDescriptionShort , Strings . S3Backend . AMZKeyDescriptionLong , null , new string [] { "auth-password" }, null ),
2021-03-07 15:38:13 -08:00
new CommandLineArgument ( "aws_access_key_id" , CommandLineArgument . ArgumentType . String , Strings . S3Backend . AMZUserIDDescriptionShort , Strings . S3Backend . AMZUserIDDescriptionLong , null , null , null , "This is deprecated, use aws-access-key-id instead" ),
2021-02-15 15:53:13 +00:00
new CommandLineArgument ( "aws-access-key-id" , CommandLineArgument . ArgumentType . String , Strings . S3Backend . AMZUserIDDescriptionShort , Strings . S3Backend . AMZUserIDDescriptionLong , null , new string [] { "auth-username" }, null ),
2015-01-20 21:07:24 +01:00
new CommandLineArgument ( EU_BUCKETS_OPTION , CommandLineArgument . ArgumentType . Boolean , Strings . S3Backend . S3EurobucketDescriptionShort , Strings . S3Backend . S3EurobucketDescriptionLong , "false" , null , null , Strings . S3Backend . S3EurobucketDeprecationDescription ( LOCATION_OPTION , S3_EU_REGION_NAME )),
2015-11-18 20:38:27 +01:00
new CommandLineArgument ( RRS_OPTION , CommandLineArgument . ArgumentType . Boolean , Strings . S3Backend . S3UseRRSDescriptionShort , Strings . S3Backend . S3UseRRSDescriptionLong , "false" , null , null , Strings . S3Backend . S3RRSDeprecationDescription ( STORAGECLASS_OPTION , S3_RRS_CLASS_NAME )),
new CommandLineArgument ( STORAGECLASS_OPTION , CommandLineArgument . ArgumentType . String , Strings . S3Backend . S3StorageclassDescriptionShort , Strings . S3Backend . S3StorageclassDescriptionLong ),
2015-01-20 21:07:24 +01:00
new CommandLineArgument ( SERVER_NAME , CommandLineArgument . ArgumentType . String , Strings . S3Backend . S3ServerNameDescriptionShort , Strings . S3Backend . S3ServerNameDescriptionLong ( hostnames . ToString ()), DEFAULT_S3_HOST ),
new CommandLineArgument ( LOCATION_OPTION , CommandLineArgument . ArgumentType . String , Strings . S3Backend . S3LocationDescriptionShort , Strings . S3Backend . S3LocationDescriptionLong ( locations . ToString ())),
2011-06-23 21:49:03 +00:00
new CommandLineArgument ( SSL_OPTION , CommandLineArgument . ArgumentType . Boolean , Strings . S3Backend . DescriptionUseSSLShort , Strings . S3Backend . DescriptionUseSSLLong ),
2019-11-25 23:46:50 +01:00
new CommandLineArgument ( S3_CLIENT_OPTION , CommandLineArgument . ArgumentType . String , Strings . S3Backend . S3ClientDescriptionShort , Strings . S3Backend . DescriptionS3ClientLong ),
2023-09-25 02:02:55 +02:00
new CommandLineArgument ( S3_DISABLE_CHUNK_ENCODING_OPTION , CommandLineArgument . ArgumentType . Boolean , Strings . S3Backend . DescriptionDisableChunkEncodingShort , Strings . S3Backend . DescriptionDisableChunkEncodingLong , "false" ),
2013-05-05 16:40:37 +02:00
new CommandLineArgument ( "auth-password" , CommandLineArgument . ArgumentType . Password , Strings . S3Backend . AuthPasswordDescriptionShort , Strings . S3Backend . AuthPasswordDescriptionLong ),
new CommandLineArgument ( "auth-username" , CommandLineArgument . ArgumentType . String , Strings . S3Backend . AuthUsernameDescriptionShort , Strings . S3Backend . AuthUsernameDescriptionLong ),
2016-02-15 13:36:10 +01:00
};
return normal . Union ( exts ). ToList ();
2009-03-21 12:46:54 +00:00
}
}
public string Description
{
get
{
2011-06-23 21:37:14 +00:00
return Strings . S3Backend . Description_v2 ;
2009-03-21 12:46:54 +00:00
}
}
2009-09-22 21:12:08 +00:00
2010-10-23 11:40:43 +00:00
public void Test ()
{
2017-09-25 23:17:45 -06:00
this . TestList ();
2010-10-23 11:40:43 +00:00
}
2009-09-22 21:12:08 +00:00
public void CreateFolder ()
{
//S3 does not complain if the bucket already exists
2011-09-27 05:12:22 +00:00
Connection . AddBucket ( m_bucket );
2009-09-22 21:12:08 +00:00
}
2010-10-23 11:40:43 +00:00
2009-01-20 22:54:53 +00:00
#endregion
2014-08-21 20:59:21 +02:00
#region IRenameEnabledBackend Members
public void Rename ( string source , string target )
{
Connection . RenameFile ( m_bucket , GetFullKey ( source ), GetFullKey ( target ));
}
#endregion
2009-01-25 19:33:36 +00:00
#region IDisposable Members
public void Dispose ()
{
2019-11-30 23:14:17 +01:00
s3Client ?. Dispose ();
2019-11-26 21:59:53 +01:00
s3Client = null ;
2009-01-25 19:33:36 +00:00
}
#endregion
2019-11-22 22:37:23 +01:00
private IS3Client Connection
2009-01-20 22:54:53 +00:00
{
2019-11-21 22:37:32 +01:00
get { return s3Client ; }
2009-01-20 22:54:53 +00:00
}
2018-02-26 11:37:10 +01:00
public string [] DNSName
2018-02-21 22:38:01 +01:00
{
2019-11-21 22:37:32 +01:00
get { return new [] { s3Client . GetDnsHost () }; }
2018-02-21 22:38:01 +01:00
}
2018-02-18 00:18:44 +01:00
2009-01-20 22:54:53 +00:00
private string GetFullKey ( string name )
{
2011-06-23 21:37:14 +00:00
//AWS SDK encodes the filenames correctly
return m_prefix + name ;
2009-01-20 22:54:53 +00:00
}
}
}