Fixed issue #127.
I am unable to reproduce the error with the "test connection" that you describe. Was it with a particular bucket/folder name? I have also added some logic to ensure that it does not repeatedly ask to prefix the username. git-svn-id: https://duplicati.googlecode.com/svn/trunk@406 59da171f-624f-0410-aa54-27559c288bec
This commit is contained in:
@@ -35,6 +35,8 @@ namespace Duplicati.Library.Backend
|
||||
private Affirma.ThreeSharp.CallingFormat m_format;
|
||||
private bool m_euBuckets;
|
||||
|
||||
private const string S3_HOST = "s3.amazonaws.com";
|
||||
|
||||
Dictionary<string, string> m_options;
|
||||
|
||||
public S3()
|
||||
@@ -86,9 +88,12 @@ namespace Duplicati.Library.Backend
|
||||
m_format = options.ContainsKey("s3-use-new-style") ? Affirma.ThreeSharp.CallingFormat.SUBDOMAIN : Affirma.ThreeSharp.CallingFormat.REGULAR;
|
||||
m_euBuckets = options.ContainsKey("s3-european-buckets");
|
||||
|
||||
if (m_host.ToLower() == "s3.amazonaws.com")
|
||||
if (m_host.ToLower() == S3_HOST)
|
||||
{
|
||||
m_bucket = u.PathAndQuery;
|
||||
m_bucket = System.Web.HttpUtility.UrlDecode(u.PathAndQuery);
|
||||
|
||||
if (m_bucket.StartsWith("/"))
|
||||
m_bucket = m_bucket.Substring(1);
|
||||
|
||||
if (m_bucket.Contains("/"))
|
||||
{
|
||||
@@ -99,27 +104,25 @@ namespace Duplicati.Library.Backend
|
||||
}
|
||||
else
|
||||
{
|
||||
//Vanity style, do a CNAME lookup
|
||||
if (!m_host.ToLower().EndsWith(".s3.amazonaws.com"))
|
||||
//If it is vanity style, do a CNAME lookup
|
||||
if (!m_host.ToLower().EndsWith("." + S3_HOST))
|
||||
{
|
||||
System.Net.IPHostEntry ent = System.Net.Dns.GetHostEntry(m_host);
|
||||
List<string> entries = new List<string>();
|
||||
entries.AddRange(ent.Aliases);
|
||||
|
||||
foreach (string s in entries)
|
||||
if (s.ToLower().EndsWith(".s3.amazonaws.com"))
|
||||
foreach (string s in ent.Aliases)
|
||||
if (s.EndsWith("." + S3_HOST, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
m_host = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_host.ToLower().EndsWith(".s3.amazonaws.com"))
|
||||
//Subdomain type lookup
|
||||
if (m_host.ToLower().EndsWith("." + S3_HOST))
|
||||
{
|
||||
m_format = Affirma.ThreeSharp.CallingFormat.SUBDOMAIN;
|
||||
m_bucket = m_host.Substring(0, m_host.Length - ".s3.amazonaws.com".Length);
|
||||
m_host = "s3.amazonaws.com";
|
||||
m_prefix = u.PathAndQuery;
|
||||
m_bucket = m_host.Substring(0, m_host.Length - ("." + S3_HOST).Length);
|
||||
m_host = S3_HOST;
|
||||
m_prefix = System.Web.HttpUtility.UrlDecode(u.PathAndQuery);
|
||||
|
||||
if (m_prefix.StartsWith("/"))
|
||||
m_prefix = m_prefix.Substring(1);
|
||||
@@ -139,7 +142,6 @@ namespace Duplicati.Library.Backend
|
||||
|
||||
private List<FileEntry> List(bool isTesting)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
S3Wrapper con = CreateRequest();
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace Duplicati.Library.Backend
|
||||
private const string PREFIX = "Prefix";
|
||||
private const string HASTESTED = "UI: HasTested";
|
||||
private const string HASCREATEDBUCKET = "UI: Has created bucket";
|
||||
private const string HASSUGGESTEDPREFIX = "UI: Has suggested prefix";
|
||||
private const string HASSUGGESTEDLOWERCASE = "UI: Has suggested lowercase";
|
||||
|
||||
private const string S3_PATH = "s3.amazonaws.com";
|
||||
|
||||
@@ -46,6 +48,8 @@ namespace Duplicati.Library.Backend
|
||||
private IDictionary<string, string> m_options;
|
||||
private bool m_hasTested;
|
||||
private bool m_hasCreatedbucket = false;
|
||||
private bool m_hasSuggestedPrefix = false;
|
||||
private bool m_hasSuggestedLowerCase = false;
|
||||
|
||||
public S3UI(IDictionary<string, string> options)
|
||||
: this()
|
||||
@@ -91,6 +95,8 @@ namespace Duplicati.Library.Backend
|
||||
m_options.Clear();
|
||||
m_options[HASTESTED] = m_hasTested.ToString();
|
||||
m_options[HASCREATEDBUCKET] = m_hasCreatedbucket.ToString();
|
||||
m_options[HASSUGGESTEDPREFIX] = m_hasSuggestedPrefix.ToString();
|
||||
m_options[HASSUGGESTEDLOWERCASE] = m_hasSuggestedLowerCase.ToString();
|
||||
|
||||
m_options[ACCESS_ID] = AWS_ID.Text;
|
||||
m_options[ACCESS_KEY] = AWS_KEY.Text;
|
||||
@@ -123,6 +129,12 @@ namespace Duplicati.Library.Backend
|
||||
|
||||
if (!m_options.ContainsKey(HASCREATEDBUCKET) || !bool.TryParse(m_options[HASCREATEDBUCKET], out m_hasCreatedbucket))
|
||||
m_hasCreatedbucket = false;
|
||||
|
||||
if (!m_options.ContainsKey(HASSUGGESTEDPREFIX) || !bool.TryParse(m_options[HASSUGGESTEDPREFIX], out m_hasSuggestedPrefix))
|
||||
m_hasSuggestedPrefix = false;
|
||||
|
||||
if (!m_options.ContainsKey(HASSUGGESTEDLOWERCASE) || !bool.TryParse(m_options[HASSUGGESTEDLOWERCASE], out m_hasSuggestedLowerCase))
|
||||
m_hasSuggestedLowerCase = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,15 +194,15 @@ namespace Duplicati.Library.Backend
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BucketName.Text.ToLower().StartsWith(AWS_ID.Text.ToLower()))
|
||||
if (!m_hasSuggestedPrefix && !BucketName.Text.ToLower().StartsWith(AWS_ID.Text.ToLower()))
|
||||
{
|
||||
DialogResult res = MessageBox.Show(this, Strings.S3UI.BucketnameNotPrefixedWarning, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
|
||||
if (res == DialogResult.Yes)
|
||||
BucketName.Text = AWS_ID.Text.ToLower() + "-" + BucketName.Text;
|
||||
else if (res == DialogResult.No)
|
||||
m_hasSuggestedPrefix = true;
|
||||
else if (res == DialogResult.Cancel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string bucketname;
|
||||
@@ -207,7 +219,7 @@ namespace Duplicati.Library.Backend
|
||||
prefix = null;
|
||||
}
|
||||
|
||||
if (bucketname.ToLower() != bucketname)
|
||||
if (!m_hasSuggestedLowerCase && bucketname.ToLower() != bucketname)
|
||||
{
|
||||
string reason = UseEuroBuckets.Checked ?
|
||||
Strings.S3UI.EuroBucketsRequireLowerCaseError :
|
||||
@@ -223,6 +235,8 @@ namespace Duplicati.Library.Backend
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (res == DialogResult.No)
|
||||
m_hasSuggestedLowerCase = true;
|
||||
}
|
||||
|
||||
if (checkForBucket)
|
||||
@@ -293,6 +307,8 @@ namespace Duplicati.Library.Backend
|
||||
{
|
||||
m_hasTested = false;
|
||||
m_hasCreatedbucket = false;
|
||||
m_hasSuggestedPrefix = false;
|
||||
m_hasSuggestedLowerCase = false;
|
||||
}
|
||||
|
||||
private void UseEuroBuckets_CheckedChanged(object sender, EventArgs e)
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Duplicati.Library.Backend
|
||||
/// </summary>
|
||||
public class S3Wrapper
|
||||
{
|
||||
private const int ITEM_LIST_LIMIT = 1000;
|
||||
private static Dictionary<string, KeyValuePair<DateTime, string>> RedirectCache = new Dictionary<string, KeyValuePair<DateTime, string>>();
|
||||
|
||||
protected const string EU_LOCATION_CONSTRAINT = "<CreateBucketConfiguration><LocationConstraint>EU</LocationConstraint></CreateBucketConfiguration>";
|
||||
@@ -138,7 +139,7 @@ namespace Duplicati.Library.Backend
|
||||
string redirUrl = GetRedirectUrl(bucketName, null);
|
||||
List<FileEntry> files = new List<FileEntry>();
|
||||
|
||||
//We truncate after 1000 elements, and then repeat
|
||||
//We truncate after ITEM_LIST_LIMIT elements, and then repeat
|
||||
while (isTruncated)
|
||||
{
|
||||
//Due to a resource leak in the S3 code, we flush it here
|
||||
@@ -149,7 +150,7 @@ namespace Duplicati.Library.Backend
|
||||
if (!string.IsNullOrEmpty(filename))
|
||||
listRequest.QueryList.Add("marker", filename);
|
||||
|
||||
listRequest.QueryList.Add("max-keys", "1000");
|
||||
listRequest.QueryList.Add("max-keys", ITEM_LIST_LIMIT.ToString());
|
||||
if (!string.IsNullOrEmpty(prefix))
|
||||
listRequest.QueryList.Add("prefix", prefix);
|
||||
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:2.0.50727.3053
|
||||
// Runtime Version:2.0.50727.4927
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@@ -97,7 +97,10 @@ namespace Duplicati.Library.Backend.Strings {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This backend can read and write data to an Amazon S3 based backend. Allowed formats are "s3://bucketname/prefix" or "s3://aws_id:aws_key@bucketname/prefix". Note that if you AWS ID or Key contains either ":", "/" or "@" you cannot use the second form, but must supply them through arguments..
|
||||
/// Looks up a localized string similar to This backend can read and write data to an Amazon S3 based backend. Allowed formats are (subdomain type): "s3://bucketname.s3.amazonaws.com/prefix" or "s3://aws_id:aws_key@bucketname.s3.amazonaws.com/prefix".
|
||||
///The url suffix model is also accepted: "s3://s3.amazonaws.com/bucketname/prefix".
|
||||
///Any hostname not using s3.amazonaws.com is considered a vanity style, and the bucket name is attempted extracted through CNAME lookup.
|
||||
///Note that if you AWS ID or Key contains either ":", "/" or "@" you cannot supply t [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string Description {
|
||||
get {
|
||||
|
||||
@@ -130,7 +130,10 @@
|
||||
<value>The AWS "Access Key ID"</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>This backend can read and write data to an Amazon S3 based backend. Allowed formats are "s3://bucketname/prefix" or "s3://aws_id:aws_key@bucketname/prefix". Note that if you AWS ID or Key contains either ":", "/" or "@" you cannot use the second form, but must supply them through arguments.</value>
|
||||
<value>This backend can read and write data to an Amazon S3 based backend. Allowed formats are (subdomain type): "s3://bucketname.s3.amazonaws.com/prefix" or "s3://aws_id:aws_key@bucketname.s3.amazonaws.com/prefix".
|
||||
The url suffix model is also accepted: "s3://s3.amazonaws.com/bucketname/prefix".
|
||||
Any hostname not using s3.amazonaws.com is considered a vanity style, and the bucket name is attempted extracted through CNAME lookup.
|
||||
Note that if you AWS ID or Key contains either ":", "/" or "@" you cannot supply them in the url, but must pass them through arguments.</value>
|
||||
</data>
|
||||
<data name="DisplayName" xml:space="preserve">
|
||||
<value>Amazon S3 based</value>
|
||||
|
||||
Reference in New Issue
Block a user