Merge branch 'warwickmm-fix_line_endings'

This commit is contained in:
Kenneth Skovhede
2018-06-04 10:45:44 +02:00
152 changed files with 2217 additions and 1118 deletions
+2 -37
View File
@@ -55,6 +55,7 @@ jobs:
- env: CATEGORY=Purge
- env: CATEGORY=Serialization
- env: CATEGORY=Utility
- env: CATEGORY=UriUtility
- env: CATEGORY=GUI
addons:
@@ -66,40 +67,4 @@ before_install:
- echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
script:
# build duplicati
- ls -la "${TRAVIS_BUILD_DIR}"/packages/
- echo "travis_fold:start:build_duplicati"
- msbuild /p:Configuration=Release Duplicati.sln
- echo "travis_fold:end:build_duplicati"
- cp -r ./Duplicati/Server/webroot ./Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/webroot
# download and extract testdata
- echo "travis_fold:start:download_extract_testdata"
- ls -la .
- if [ ! -d ~/tmp ]; then mkdir ~/tmp; fi
- if [ ! -d ~/download ]; then mkdir ~/download; fi
- if [ ! -d ~/download/svn ]; then mkdir ~/download/svn; fi
- if [ ! -d ~/download/bulk ]; then mkdir ~/download/bulk; fi
- if [ ! -f ~/download/svn/DSMCBE.zip ] && ([ "$CATEGORY" == "SVNData" ] || [ "$CATEGORY" == "SVNDataLong" ]); then wget --progress=dot:giga "https://s3.amazonaws.com/duplicati-test-file-hosting/DSMCBE.zip" -O ~/download/svn/DSMCBE.zip; fi
- if [ ! -f ~/download/bulk/data.zip ] && ([ "$CATEGORY" == "BulkNormal" ] || [ "$CATEGORY" == "BulkNoSize" ]); then wget --progress=dot:giga "https://s3.amazonaws.com/duplicati-test-file-hosting/data.zip" -O ~/download/bulk/data.zip; fi
- ls -la .
- mkdir ~/testdata
- mkdir ~/testdata/data
- mkdir ~/testdata/DSMCBE
- if [ "$CATEGORY" == "SVNData" ] || [ "$CATEGORY" == "SVNDataLong" ]; then unzip -q ~/download/svn/DSMCBE.zip -d ~/testdata/; fi
- if [ "$CATEGORY" == "BulkNormal" ] || [ "$CATEGORY" == "BulkNoSize" ]; then unzip -q ~/download/bulk/data.zip -d ~/testdata/; fi
- chown -R travis ~/testdata/
- chmod -R 755 ~/testdata
- ls -la ~/testdata
- ls -la ~/testdata/DSMCBE
- ls -la ~/testdata/data
- echo "travis_fold:end:download_extract_testdata"
# run unit tests
- echo "travis_fold:start:unit_test"
- if [[ "$CATEGORY" != "GUI" ]]; then mono ./testrunner/NUnit.ConsoleRunner.3.5.0/tools/nunit3-console.exe ./Duplicati/UnitTest/bin/Release/Duplicati.UnitTest.dll --where:cat==$CATEGORY --workers=1; fi
- echo "travis_fold:end:unit_test"
# start server and run gui tests
- echo "travis_fold:start:gui_unit_test"
- mono ./Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release/Duplicati.Server.exe &
- if [[ "$CATEGORY" == "GUI" ]]; then python guiTests/guiTest.py; fi
- echo "travis_fold:end:gui_unit_test"
- ./build.sh ${TRAVIS_BUILD_DIR} ${CATEGORY}
@@ -20,7 +20,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Duplicati.Library.Interface;
namespace Duplicati.CommandLine.BackendTool
+6 -10
View File
@@ -27,12 +27,12 @@ namespace Duplicati.CommandLine
private class PeriodicOutput : IDisposable
{
public event Action<float, long, long, bool> WriteOutput;
private System.Threading.ManualResetEvent m_readyEvent;
private System.Threading.ManualResetEvent m_finishEvent;
private ConsoleOutput m_output;
private readonly System.Threading.ManualResetEvent m_readyEvent;
private readonly System.Threading.ManualResetEvent m_finishEvent;
private readonly ConsoleOutput m_output;
private System.Threading.Thread m_thread;
private TimeSpan m_updateFrequency;
private readonly TimeSpan m_updateFrequency;
public PeriodicOutput(ConsoleOutput messageSink, TimeSpan updateFrequency)
{
@@ -133,11 +133,7 @@ namespace Duplicati.CommandLine
public static int Help(TextWriter outwriter, Action<Duplicati.Library.Main.Controller> setup, List<string> args, Dictionary<string, string> options, Library.Utility.IFilter filter)
{
if (args.Count < 1)
Duplicati.CommandLine.Help.PrintUsage(outwriter, "help", options);
else
Duplicati.CommandLine.Help.PrintUsage(outwriter, args[0], options);
Duplicati.CommandLine.Help.PrintUsage(outwriter, args.Count > 1? args[1] : "help", options);
return 0;
}
+9 -7
View File
@@ -409,10 +409,10 @@ namespace Duplicati.CommandLine
string c = s;
string leadingSpaces = "";
StringBuilder leadingSpaces = new StringBuilder();
while (c.Length > 0 && c.StartsWith(" ", StringComparison.Ordinal))
{
leadingSpaces += " ";
leadingSpaces.Append(" ");
c = c.Remove(0, 1);
}
@@ -434,7 +434,7 @@ namespace Duplicati.CommandLine
if (extraIndent)
{
extraIndent = false;
leadingSpaces += " ";
leadingSpaces.Append(" ");
}
}
}
@@ -442,13 +442,15 @@ namespace Duplicati.CommandLine
private class Matcher
{
Dictionary<string, Library.Interface.ICommandLineArgument> args = new Dictionary<string, Library.Interface.ICommandLineArgument>(StringComparer.OrdinalIgnoreCase);
readonly Dictionary<string, Library.Interface.ICommandLineArgument> args = new Dictionary<string, Library.Interface.ICommandLineArgument>(StringComparer.OrdinalIgnoreCase);
public Matcher()
{
List<IList<Library.Interface.ICommandLineArgument>> foundArgs = new List<IList<Library.Interface.ICommandLineArgument>>();
foundArgs.Add(new Library.Main.Options(new Dictionary<string, string>()).SupportedCommands);
foundArgs.Add(Program.SupportedOptions);
List<IList<Library.Interface.ICommandLineArgument>> foundArgs = new List<IList<Library.Interface.ICommandLineArgument>>
{
new Library.Main.Options(new Dictionary<string, string>()).SupportedCommands,
Program.SupportedOptions
};
foreach (Duplicati.Library.Interface.IBackend backend in Library.DynamicLoader.BackendLoader.Backends)
if (backend.SupportedCommands != null)
+12 -24
View File
@@ -29,7 +29,7 @@ namespace Duplicati.CommandLine
{
public class Program
{
private static string LOGTAG = Library.Logging.Log.LogTagFromType<Program>();
private static readonly string LOGTAG = Library.Logging.Log.LogTagFromType<Program>();
public static bool FROM_COMMANDLINE = false;
@@ -163,34 +163,22 @@ namespace Duplicati.CommandLine
}
// Probe for "help" to avoid extra processing
bool isHelp = cargs.Count == 0 || (cargs.Count >= 1 && string.Equals(cargs[0], "help", StringComparison.OrdinalIgnoreCase));
if (isHelp)
if (cargs.Count == 0 || (string.Equals(cargs[0], "help", StringComparison.OrdinalIgnoreCase)))
{
return Commands.Help(outwriter, setup, cargs, options, filter);
}
if ((options.ContainsKey("parameters-file") && !string.IsNullOrEmpty("parameters-file")) || (options.ContainsKey("parameter-file") && !string.IsNullOrEmpty("parameter-file")) || (options.ContainsKey("parameterfile") && !string.IsNullOrEmpty("parameterfile")))
// try and parse all parameter file aliases
foreach (string parameterOption in new []{ "parameters-file", "parameters-file", "parameterfile"} )
{
string filename;
if (options.ContainsKey("parameters-file") && !string.IsNullOrEmpty("parameters-file"))
if (options.ContainsKey(parameterOption) && !string.IsNullOrEmpty(options[parameterOption]))
{
filename = options["parameters-file"];
options.Remove("parameters-file");
}
else if (options.ContainsKey("parameter-file") && !string.IsNullOrEmpty("parameter-file"))
{
filename = options["parameter-file"];
options.Remove("parameter-file");
}
else
{
filename = options["parameterfile"];
options.Remove("parameterfile");
}
if (!ReadOptionsFromFile(outwriter, filename, ref filter, cargs, options))
return 100;
string filename = options[parameterOption];
options.Remove(parameterOption);
if (!ReadOptionsFromFile(outwriter, filename, ref filter, cargs, options))
return 100;
break;
}
}
if (!options.ContainsKey("passphrase"))
@@ -204,7 +192,7 @@ namespace Duplicati.CommandLine
if (!options.ContainsKey("auth-username"))
if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("AUTH_USERNAME")))
options["auth-username"] = System.Environment.GetEnvironmentVariable("AUTH_USERNAME");
var showDeletionErrors = verboseErrors;
Duplicati.Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) =>
{
@@ -1,4 +1,4 @@
// Copyright (C) 2015, The Duplicati Team
// 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
@@ -268,14 +268,14 @@ namespace Duplicati.CommandLine.RecoveryTool
{
private const int LOOKUP_TABLE_SIZE = 2048;
private CompressedFileMRUCache m_cache;
private int m_hashsize;
private int m_blocksize;
private readonly CompressedFileMRUCache m_cache;
private readonly int m_hashsize;
private readonly int m_blocksize;
private Stream m_indexfile;
private List<string> m_lookup = new List<string>();
private List<long> m_offsets = new List<long>();
private byte[] m_linebuf = new byte[128];
private byte[] m_newline = Environment.NewLine.Select(x => (byte)x).ToArray();
private readonly List<string> m_lookup = new List<string>();
private readonly List<long> m_offsets = new List<long>();
private readonly byte[] m_linebuf = new byte[128];
private readonly byte[] m_newline = Environment.NewLine.Select(x => (byte)x).ToArray();
public HashLookupHelper(string indexfile, CompressedFileMRUCache cache, int blocksize, int hashsize)
{
@@ -432,7 +432,7 @@ namespace Duplicati.CommandLine.RecoveryTool
private Dictionary<string, Library.Interface.ICompression> m_lookup = new Dictionary<string, Duplicati.Library.Interface.ICompression>();
private Dictionary<string, Stream> m_streams = new Dictionary<string, Stream>();
private List<string> m_mru = new List<string>();
private Dictionary<string, string> m_options;
private readonly Dictionary<string, string> m_options;
private const int MAX_OPEN_ARCHIVES = 20;
+2 -2
View File
@@ -22,9 +22,9 @@ namespace Duplicati.CommandLine.Strings {
public static string ParametersFileOptionShort { get { return LC.L(@"Path to a file with parameters"); } }
public static string UnhandledException(string message) { return LC.L(@"An error occured: {0}", message); }
public static string UnhandledInnerException(string message) { return LC.L(@"The inner error message is: {0}", message); }
public static string IncludeLong { get { return LC.L(@"Include files that match this filter. The special character * means any number of character, and the special character ? means any single character, use *.txt to include all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, i.e. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, i.e. {Applications}."); } }
public static string IncludeLong { get { return LC.L(@"Include files that match this filter. The special character * means any number of character, and the special character ? means any single character, use *.txt to include all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, i.e. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, i.e. {{Applications}}."); } }
public static string IncludeShort { get { return LC.L(@"Include files"); } }
public static string ExcludeLong { get { return LC.L(@"Exclude files that match this filter. The special character * means any number of character, and the special character ? means any single character, use *.txt to exclude all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, i.e. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, i.e. {TemporaryFiles}."); } }
public static string ExcludeLong { get { return LC.L(@"Exclude files that match this filter. The special character * means any number of character, and the special character ? means any single character, use *.txt to exclude all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, i.e. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, i.e. {{TemporaryFiles}}."); } }
public static string ExcludeShort { get { return LC.L(@"Exclude files"); } }
public static string ControlFilesOptionShort { get { return LC.L(@"If this option is used with a backup operation, it is interpreted as a list of files to add to the filesets. When used with list or restore, it will list or restore the control files instead of the normal files."); } }
public static string ControlFilesOptionLong { get { return LC.L(@"Use control files"); } }
+4 -2
View File
@@ -292,8 +292,10 @@ Duplicati provides information for debugging and logging purposes. By default, a
If something needs to be retried (e.g. upload failed) this will cause an entry in the log file.
--log-file = <path>
The path to the log file e.g. "D:\duplicati\log.txt".
--log-level = Profiling | Information | Warning | Error
Specifies the log level.
--log-file-log-level = Profiling | Verbose | Information | Warning | Error
Specifies the log level to use for the log file.
--console-log-level = Profiling | Verbose | Information | Warning | Error
Specifies the log level to use for the console log.
# =============================================================================
@@ -28,8 +28,8 @@ namespace Duplicati.GUI.TrayIcon
{
private class MenuItemWrapper : Duplicati.GUI.TrayIcon.IMenuItem
{
private NSMenuItem m_item;
private Action m_callback;
private readonly NSMenuItem m_item;
private readonly Action m_callback;
public NSMenuItem MenuItem { get { return m_item; } }
@@ -93,11 +93,11 @@ namespace Duplicati.GUI.TrayIcon
private static readonly string ICON_ERROR = ICON_PATH + "normal-error.png";
private NSStatusItem m_statusItem;
private Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, NSImage> m_images = new Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, NSImage>();
private readonly Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, NSImage> m_images = new Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, NSImage>();
private NSApplication m_app;
// We need to keep the items around, otherwise the GC will destroy them and crash the app
private List<Duplicati.GUI.TrayIcon.IMenuItem> m_keeper = new List<Duplicati.GUI.TrayIcon.IMenuItem>();
private readonly List<Duplicati.GUI.TrayIcon.IMenuItem> m_keeper = new List<Duplicati.GUI.TrayIcon.IMenuItem>();
public override void Init(string[] args)
{
@@ -10,7 +10,7 @@ namespace Duplicati.GUI.TrayIcon
/// </summary>
internal class HostedInstanceKeeper : IDisposable
{
private System.Threading.Thread m_runner;
private readonly System.Threading.Thread m_runner;
private System.Exception m_runnerException = null;
public event Action InstanceShutdown;
@@ -38,10 +38,10 @@ namespace Duplicati.GUI.TrayIcon
}
}
private string m_apiUri;
private string m_baseUri;
private readonly string m_apiUri;
private readonly string m_baseUri;
private string m_password;
private bool m_saltedpassword;
private readonly bool m_saltedpassword;
private string m_authtoken;
private string m_xsrftoken;
private static readonly System.Text.Encoding ENCODING = System.Text.Encoding.GetEncoding("utf-8");
@@ -59,7 +59,7 @@ namespace Duplicati.GUI.TrayIcon
private volatile bool m_shutdown = false;
private volatile System.Threading.Thread m_requestThread;
private volatile System.Threading.Thread m_pollThread;
private System.Threading.AutoResetEvent m_waitLock;
private readonly System.Threading.AutoResetEvent m_waitLock;
private readonly Dictionary<string, string> m_updateRequest;
private readonly Dictionary<string, string> m_options;
@@ -69,7 +69,7 @@ namespace Duplicati.GUI.TrayIcon
public IServerStatus Status { get { return m_status; } }
private readonly object m_lock = new object();
private Queue<BackgroundRequest> m_workQueue = new Queue<BackgroundRequest>();
private readonly Queue<BackgroundRequest> m_workQueue = new Queue<BackgroundRequest>();
public HttpServerConnection(Uri server, string password, bool saltedpassword, Program.PasswordSource passwordSource, Dictionary<string, string> options)
{
@@ -29,7 +29,7 @@ namespace Duplicati.GUI.TrayIcon
private class MenuItemWrapper : Duplicati.GUI.TrayIcon.IMenuItem
{
private RumpsRunner m_parent;
private readonly RumpsRunner m_parent;
private string m_text;
private MenuIcons m_icon;
@@ -115,7 +115,7 @@ namespace Duplicati.GUI.TrayIcon
private static readonly string ICON_RUNNING = ICON_PATH + "normal-running.png";
private static readonly string ICON_ERROR = ICON_PATH + "normal-error.png";
private Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, string> m_images = new Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, string>();
private readonly Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, string> m_images = new Dictionary<Duplicati.GUI.TrayIcon.TrayIcons, string>();
private System.Diagnostics.Process m_rumpsProcess;
@@ -134,7 +134,7 @@ namespace Duplicati.GUI.TrayIcon
public virtual IBrowserWindow ShowUrlInWindow(string url)
{
//Fallback is to just show the window in a browser
Duplicati.Library.Utility.UrlUtillity.OpenURL(url, Program.BrowserCommand);
Duplicati.Library.Utility.UrlUtility.OpenURL(url, Program.BrowserCommand);
return null;
}
@@ -27,8 +27,8 @@ namespace Duplicati.GUI.TrayIcon.Windows
{
private class MenuItemWrapper : IMenuItem
{
private ToolStripItem m_menu;
private Action m_callback;
private readonly ToolStripItem m_menu;
private readonly Action m_callback;
public ToolStripItem MenuItem { get { return m_menu; } }
@@ -30,7 +30,7 @@ namespace Duplicati.Library.AutoUpdater
/// <summary>
/// The stream to read from
/// </summary>
private System.IO.Stream m_stream;
private readonly System.IO.Stream m_stream;
protected SignatureReadingStream()
{
@@ -616,7 +616,7 @@ namespace Duplicati.Library.AutoUpdater
if (string.IsNullOrWhiteSpace(relpath))
continue;
if (IgnoreWebrootFolder && relpath.StartsWith("webroot", Library.Utility.Utility.ClientFilenameStringComparision))
if (IgnoreWebrootFolder && relpath.StartsWith("webroot", Library.Utility.Utility.ClientFilenameStringComparison))
continue;
FileEntry fe;
@@ -624,7 +624,7 @@ namespace Duplicati.Library.AutoUpdater
{
var ignore = false;
foreach (var c in ignores)
if (ignore = relpath.StartsWith(c, Library.Utility.Utility.ClientFilenameStringComparision))
if (ignore = relpath.StartsWith(c, Library.Utility.Utility.ClientFilenameStringComparison))
break;
if (ignore)
@@ -654,7 +654,7 @@ namespace Duplicati.Library.AutoUpdater
var filteredpaths = paths
.Where(p => !string.IsNullOrWhiteSpace(p.Key) && !p.Key.EndsWith("/", StringComparison.Ordinal))
.Where(p => !IgnoreWebrootFolder || !p.Key.StartsWith("webroot", Library.Utility.Utility.ClientFilenameStringComparision))
.Where(p => !IgnoreWebrootFolder || !p.Key.StartsWith("webroot", Library.Utility.Utility.ClientFilenameStringComparison))
.Select(p => p.Key)
.ToList();
@@ -45,14 +45,14 @@ namespace Duplicati.Library.Backend.AmazonCloudDrive
private EndpointInfo m_endPointInfo;
private ResourceModel m_curdir;
private string m_path;
private string[] m_labels;
private readonly string m_path;
private readonly string[] m_labels;
private OAuthHelper m_oauth;
private readonly OAuthHelper m_oauth;
private Dictionary<string, string> m_filecache;
private string m_userid;
private readonly string m_userid;
private DateTime m_waitUntil;
private TimeSpan m_delayTimeSpan;
private readonly TimeSpan m_delayTimeSpan;
private enum RemoteOperation
{
+6 -6
View File
@@ -35,12 +35,12 @@ namespace Duplicati.Library.Backend.Backblaze
private const int DEFAULT_PAGE_SIZE = 500;
private string m_bucketname;
private string m_prefix;
private string m_urlencodedprefix;
private string m_bucketType;
private int m_pagesize;
private B2AuthHelper m_helper;
private readonly string m_bucketname;
private readonly string m_prefix;
private readonly string m_urlencodedprefix;
private readonly string m_bucketType;
private readonly int m_pagesize;
private readonly B2AuthHelper m_helper;
private UploadUrlResponse m_uploadUrl;
private Dictionary<string, List<FileEntity>> m_filecache;
+3 -3
View File
@@ -35,9 +35,9 @@ namespace Duplicati.Library.Backend.Box
private const int PAGE_SIZE = 200;
private BoxHelper m_oauth;
private string m_path;
private bool m_deleteFromTrash;
private readonly BoxHelper m_oauth;
private readonly string m_path;
private readonly bool m_deleteFromTrash;
private string m_currentfolder;
private Dictionary<string, string> m_filecache;
@@ -39,13 +39,13 @@ namespace Duplicati.Library.Backend
private const int ITEM_LIST_LIMIT = 1000;
private string m_username;
private string m_password;
private string m_path;
private readonly string m_username;
private readonly string m_password;
private readonly string m_path;
private string m_storageUrl = null;
private string m_authToken = null;
private string m_authUrl;
private readonly string m_authUrl;
private readonly byte[] m_copybuffer = new byte[Duplicati.Library.Utility.Utility.DEFAULT_BUFFER_SIZE];
+3 -2
View File
@@ -10,8 +10,9 @@ namespace Duplicati.Library.Backend
private const string AUTHID_OPTION = "authid";
private const int MAX_FILE_LIST = 10000;
private string m_path,m_accesToken;
private DropboxHelper dbx;
private readonly string m_accesToken;
private readonly string m_path;
private readonly DropboxHelper dbx;
public Dropbox()
{
@@ -32,12 +32,12 @@ namespace Duplicati.Library.Backend
private const string OPTION_MOVE_FILE = "use-move-for-put";
private const string OPTION_FORCE_REAUTH = "force-smb-authentication";
private string m_path;
private readonly string m_path;
private string m_username;
private string m_password;
private bool m_moveFile;
private readonly bool m_moveFile;
private bool m_hasAutenticated;
private bool m_forceReauth;
private readonly bool m_forceReauth;
private readonly byte[] m_copybuffer = new byte[Duplicati.Library.Utility.Utility.DEFAULT_BUFFER_SIZE];
@@ -41,6 +41,7 @@
<Compile Include="GoogleCommon.cs" />
<Compile Include="GoogleDrive.cs" />
<Compile Include="GCSConfig.cs" />
<Compile Include="WebApi.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@@ -14,15 +14,69 @@
// 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;
using System;
using Duplicati.Library.Interface;
using System.Collections.Generic;
using System.Linq;
namespace Duplicati.Library.Backend.GoogleServices
{
public class GCSConfig : IWebModule
namespace Duplicati.Library.Backend.GoogleServices
{
private const ConfigType DEFAULT_CONFIG_TYPE = ConfigType.Locations;
private static readonly string DEFAULT_CONFIG_TYPE_STR = Enum.GetName(typeof(ConfigType), DEFAULT_CONFIG_TYPE);
private const string KEY_CONFIGTYPE = "gcs-config";
public enum ConfigType
{
Locations,
StorageClasses
}
public GCSConfig()
{
}
private const ConfigType DEFAULT_CONFIG_TYPE = ConfigType.Locations;
#region IWebModule implementation
public System.Collections.Generic.IDictionary<string, string> Execute(System.Collections.Generic.IDictionary<string, string> options)
{
string k;
options.TryGetValue(KEY_CONFIGTYPE, out k);
if (string.IsNullOrWhiteSpace(k))
k = DEFAULT_CONFIG_TYPE_STR;
ConfigType ct;
if (!Enum.TryParse<ConfigType>(k, true, out ct))
ct = DEFAULT_CONFIG_TYPE;
switch (ct)
{
case ConfigType.StorageClasses:
return GoogleCloudStorage.GoogleCloudStorage.KNOWN_GCS_STORAGE_CLASSES.ToDictionary((x) => x.Key, (y) => y.Value);
default:
return GoogleCloudStorage.GoogleCloudStorage.KNOWN_GCS_LOCATIONS.ToDictionary((x) => x.Key, (y) => y.Value);
}
}
public string Key { get { return "gcs-getconfig"; } }
public string DisplayName { get { return "Google Cloud Storage configuration module"; } }
public string Description { get { return "Exposes Google Cloud Storage configuration as a web module"; } }
public System.Collections.Generic.IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, "The config to get", "Provides different config values", DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
});
}
}
#endregion
}
}
@@ -18,7 +18,6 @@ using System;
using Duplicati.Library.Interface;
using System.Collections.Generic;
using System.Net;
using System.Web;
using Duplicati.Library.Utility;
using Newtonsoft.Json;
using System.Text;
@@ -62,13 +61,13 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
new KeyValuePair<string, string>("Nearline", "NEARLINE"),
};
private string m_bucket;
private string m_prefix;
private string m_project;
private OAuthHelper m_oauth;
private readonly string m_bucket;
private readonly string m_prefix;
private readonly string m_project;
private readonly OAuthHelper m_oauth;
private string m_location;
private string m_storage_class;
private readonly string m_location;
private readonly string m_storage_class;
public GoogleCloudStorage()
{
}
@@ -97,9 +96,9 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
m_oauth = new OAuthHelper(authid, this.ProtocolKey);
m_oauth.AutoAuthHeader = true;
}
}
private class ListBucketResponse
{
public string kind { get; set; }
@@ -205,7 +204,8 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
if (string.IsNullOrEmpty(m_project))
throw new UserInformationException(Strings.GoogleCloudStorage.ProjectIDMissingError(PROJECT_OPTION), "GoogleCloudStorageMissingProjectID");
var data = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new CreateBucketRequest() {
var data = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new CreateBucketRequest
{
name = m_bucket,
location = m_location,
storageClass = m_storage_class
@@ -220,7 +220,7 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
var areq = new AsyncHttpRequest(req);
using(var rs = areq.GetRequestStream())
using (var rs = areq.GetRequestStream())
rs.Write(data, 0, data.Length);
m_oauth.ReadJSONResponse<BucketResourceItem>(areq);
@@ -246,8 +246,8 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
foreach (KeyValuePair<string, string> s in KNOWN_GCS_LOCATIONS)
locations.AppendLine(string.Format("{0}: {1}", s.Key, s.Value));
foreach (KeyValuePair<string, string> s in KNOWN_GCS_STORAGE_CLASSES)
storageClasses.AppendLine(string.Format("{0}: {1}", s.Key, s.Value));
storageClasses.AppendLine(string.Format("{0}: {1}", s.Key, s.Value));
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(LOCATION_OPTION, CommandLineArgument.ArgumentType.String, Strings.GoogleCloudStorage.LocationDescriptionShort, Strings.GoogleCloudStorage.LocationDescriptionLong(locations.ToString())),
new CommandLineArgument(STORAGECLASS_OPTION, CommandLineArgument.ArgumentType.String, Strings.GoogleCloudStorage.StorageclassDescriptionShort, Strings.GoogleCloudStorage.StorageclassDescriptionLong(locations.ToString())),
@@ -276,8 +276,8 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
var res = GoogleCommon.ChunckedUploadWithResume<BucketResourceItem, BucketResourceItem>(m_oauth, item, url, stream);
if (res == null)
throw new Exception(string.Format("Upload succeeded, but no data was returned"));
throw new Exception("Upload succeeded, but no data was returned");
}
public void Get(string remotename, System.IO.Stream stream)
@@ -288,8 +288,8 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
var req = m_oauth.CreateRequest(url);
var areq = new AsyncHttpRequest(req);
using(var resp = areq.GetResponse())
using(var rs = areq.GetResponseStream())
using (var resp = areq.GetResponse())
using (var rs = areq.GetResponseStream())
Library.Utility.Utility.CopyStream(rs, stream);
}
catch (WebException wex)
@@ -304,7 +304,8 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
public void Rename(string oldname, string newname)
{
var data = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new BucketResourceItem() {
var data = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new BucketResourceItem
{
name = m_prefix + newname,
}));
@@ -315,7 +316,7 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
req.ContentType = "application/json; charset=UTF-8";
var areq = new AsyncHttpRequest(req);
using(var rs = areq.GetRequestStream())
using (var rs = areq.GetRequestStream())
rs.Write(data, 0, data.Length);
m_oauth.ReadJSONResponse<BucketResourceItem>(req);
@@ -323,8 +324,8 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
#region IDisposable implementation
public void Dispose()
{
{
}
#endregion
}
File diff suppressed because one or more lines are too long
@@ -15,14 +15,18 @@
// 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;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using Newtonsoft.Json;
using Duplicati.Library.Backend.GoogleServices;
using Duplicati.Library.Interface;
using Duplicati.Library.Utility;
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;
using Duplicati.Library.Backend.GoogleServices;
using System.Text;
namespace Duplicati.Library.Backend.GoogleDrive
{
public class GoogleDrive : IBackend, IStreamingBackend, IQuotaEnabledBackend, IRenameEnabledBackend
@@ -31,15 +35,13 @@ namespace Duplicati.Library.Backend.GoogleDrive
private const string DISABLE_TEAMDRIVE_OPTION = "googledrive-disable-teamdrive";
private const string FOLDER_MIMETYPE = "application/vnd.google-apps.folder";
private const string DRIVE_API_UPLOAD_URL = "https://www.googleapis.com/upload/drive/v2";
private const string DRIVE_API_URL = "https://www.googleapis.com/drive/v2";
private readonly string m_path;
private readonly bool m_useTeamDrive = true;
private string m_path;
private bool m_useTeamDrive = true;
private OAuthHelper m_oauth;
private readonly OAuthHelper m_oauth;
private string m_currentFolderId;
private Dictionary<string, GoogleDriveFolderItem[]> m_filecache;
private readonly Dictionary<string, GoogleDriveFolderItem[]> m_filecache;
public GoogleDrive()
{
@@ -66,26 +68,30 @@ namespace Duplicati.Library.Backend.GoogleDrive
private string GetFolderId(string path, bool autocreate = false)
{
var curparent = GetAboutInfo().rootFolderId;
var curdisplay = "/";
var curparent = GetAboutInfo().rootFolderId;
var curdisplay = new StringBuilder("/");
foreach(var p in path.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries))
foreach (var p in path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries))
{
var res = ListFolder(curparent, true, p).ToArray();
if (res.Length == 0)
{
if (!autocreate)
throw new FolderMissingException();
curparent = CreateFolder(p, curparent).id;
}
else if (res.Length > 1)
throw new UserInformationException(Strings.GoogleDrive.MultipleEntries(p, curdisplay), "GoogleDriveMultipleEntries");
else
curparent = res[0].id;
curdisplay += p + "/";
var res = ListFolder(curparent, true, p).ToArray();
if (res.Length == 0)
{
if (!autocreate)
throw new FolderMissingException();
curparent = CreateFolder(p, curparent).id;
}
else if (res.Length > 1)
{
throw new UserInformationException(Strings.GoogleDrive.MultipleEntries(p, curdisplay.ToString()), "GoogleDriveMultipleEntries");
}
else
{
curparent = res[0].id;
}
curdisplay.Append(p).Append("/");
}
return curparent;
@@ -110,17 +116,17 @@ namespace Duplicati.Library.Backend.GoogleDrive
if (entries != null)
return entries;
var list = ListFolder(CurrentFolderId, false, remotename).ToArray();
entries = ListFolder(CurrentFolderId, false, remotename).ToArray();
if (list == null || list.Length == 0)
if (entries == null || entries.Length == 0)
{
if (throwMissingException)
throw new FileMissingException();
else
return null;
}
return m_filecache[remotename] = list;
return m_filecache[remotename] = entries;
}
private static string EscapeTitleEntries(string title)
@@ -141,27 +147,32 @@ namespace Duplicati.Library.Backend.GoogleDrive
GoogleDriveFolderItem[] files;
m_filecache.TryGetValue(remotename, out files);
string fileid = null;
string fileId = null;
if (files != null)
{
if (files.Length == 1)
fileid = files[0].id;
fileId = files[0].id;
else
Delete(remotename);
}
var isUpdate = !string.IsNullOrWhiteSpace(fileid);
var isUpdate = !string.IsNullOrWhiteSpace(fileId);
var values = new NameValueCollection {
{ WebApi.GoogleDrive.QueryParam.UploadType,
WebApi.GoogleDrive.QueryValue.Resumable } };
var url = isUpdate ?
string.Format("{0}/files/{1}?uploadType=resumable", DRIVE_API_UPLOAD_URL, Library.Utility.Uri.UrlPathEncode(fileid)) :
string.Format("{0}/files?uploadType=resumable", DRIVE_API_UPLOAD_URL);
WebApi.GoogleDrive.FileUploadUrl(Library.Utility.Uri.UrlPathEncode(fileId), values) :
WebApi.GoogleDrive.FileUploadUrl(values);
var item = new GoogleDriveFolderItem() {
var item = new GoogleDriveFolderItem
{
title = remotename,
description = remotename,
mimeType = "application/octet-stream",
labels = new GoogleDriveFolderItemLabels { hidden = true },
parents = new GoogleDriveParentReference[] { new GoogleDriveParentReference() { id = CurrentFolderId } }
parents = new GoogleDriveParentReference[] { new GoogleDriveParentReference { id = CurrentFolderId } }
};
var res = GoogleCommon.ChunckedUploadWithResume<GoogleDriveFolderItem, GoogleDriveFolderItem>(m_oauth, item, url, stream, isUpdate ? "PUT" : "POST");
@@ -172,7 +183,6 @@ namespace Duplicati.Library.Backend.GoogleDrive
m_filecache.Clear();
throw;
}
}
public void Get(string remotename, System.IO.Stream stream)
@@ -181,12 +191,15 @@ namespace Duplicati.Library.Backend.GoogleDrive
if (m_filecache.Count == 0)
foreach (var file in List()) { /* Enumerate the full listing */ }
var fileid = GetFileEntries(remotename).OrderByDescending(x => x.createdDate).First().id;
var fileId = GetFileEntries(remotename).OrderByDescending(x => x.createdDate).First().id;
var req = m_oauth.CreateRequest(string.Format("{0}/files/{1}?alt=media{2}", DRIVE_API_URL, fileid, m_useTeamDrive ? "&supportsTeamDrives=true" : string.Empty));
var url = WebApi.GoogleDrive.FileQueryUrl(fileId, new NameValueCollection{
{ WebApi.GoogleDrive.QueryParam.Alt, WebApi.GoogleDrive.QueryValue.Media }
});
var req = m_oauth.CreateRequest(url);
var areq = new AsyncHttpRequest(req);
using(var resp = (HttpWebResponse)areq.GetResponse())
using(var rs = areq.GetResponseStream())
using (var resp = (HttpWebResponse)areq.GetResponse())
using (var rs = areq.GetResponseStream())
Duplicati.Library.Utility.Utility.CopyStream(rs, stream);
}
@@ -247,7 +260,7 @@ namespace Duplicati.Library.Backend.GoogleDrive
}
}
}
public void Put(string remotename, string filename)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
@@ -264,11 +277,11 @@ namespace Duplicati.Library.Backend.GoogleDrive
{
try
{
foreach(var fileid in from n in GetFileEntries(remotename) select n.id)
foreach (var fileid in from n in GetFileEntries(remotename) select n.id)
{
var url = string.Format("{0}/files/{1}{2}", DRIVE_API_URL, Library.Utility.Uri.UrlPathEncode(fileid), m_useTeamDrive ? "?supportsTeamDrives=true" : string.Empty);
m_oauth.GetJSONData<object>(url, x => {
var url = WebApi.GoogleDrive.FileQueryUrl(Library.Utility.Uri.UrlPathEncode(fileid), SupportsTeamDriveParam());
m_oauth.GetJSONData<object>(url, x =>
{
x.Method = "DELETE";
});
}
@@ -310,15 +323,16 @@ namespace Duplicati.Library.Backend.GoogleDrive
}
}
public System.Collections.Generic.IList<ICommandLineArgument> SupportedCommands
{
get {
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(AUTHID_OPTION, CommandLineArgument.ArgumentType.Password, Strings.GoogleDrive.AuthidShort, Strings.GoogleDrive.AuthidLong(OAuthHelper.OAUTH_LOGIN_URL("googledrive"))),
new CommandLineArgument(DISABLE_TEAMDRIVE_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.GoogleDrive.DisableTeamDriveShort, Strings.GoogleDrive.DisableTeamDriveLong),
public System.Collections.Generic.IList<ICommandLineArgument> SupportedCommands => new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(AUTHID_OPTION,
CommandLineArgument.ArgumentType.Password,
Strings.GoogleDrive.AuthidShort,
Strings.GoogleDrive.AuthidLong(OAuthHelper.OAUTH_LOGIN_URL("googledrive"))),
new CommandLineArgument(DISABLE_TEAMDRIVE_OPTION,
CommandLineArgument.ArgumentType.Boolean,
Strings.GoogleDrive.DisableTeamDriveShort,
Strings.GoogleDrive.DisableTeamDriveLong),
});
}
}
public string Description
{
@@ -348,8 +362,8 @@ namespace Duplicati.Library.Backend.GoogleDrive
}
public string[] DNSName
{
get { return new string[] { new System.Uri(DRIVE_API_URL).Host, new System.Uri(DRIVE_API_UPLOAD_URL).Host }; }
{
get { return new string[] { new System.Uri(WebApi.GoogleDrive.Url.DRIVE).Host, new System.Uri(WebApi.GoogleDrive.Url.UPLOAD).Host }; }
}
#endregion
@@ -365,21 +379,22 @@ namespace Duplicati.Library.Backend.GoogleDrive
var newfile = JsonConvert.DeserializeObject<GoogleDriveFolderItem>(JsonConvert.SerializeObject(files[0]));
newfile.title = newname;
newfile.parents = new GoogleDriveParentReference[] { new GoogleDriveParentReference() { id = CurrentFolderId } };
newfile.parents = new GoogleDriveParentReference[] { new GoogleDriveParentReference { id = CurrentFolderId } };
var url = string.Format("{0}/files/{1}", DRIVE_API_UPLOAD_URL, Library.Utility.Uri.UrlPathEncode(files[0].id));
var url = WebApi.GoogleDrive.FileQueryUrl(Library.Utility.Uri.UrlPathEncode(files[0].id));
var data = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(newfile));
var nf = m_oauth.GetJSONData<GoogleDriveFolderItem>(url, x => {
var nf = m_oauth.GetJSONData<GoogleDriveFolderItem>(url, x =>
{
x.Method = "PUT";
x.ContentLength = data.Length;
x.ContentType = "application/json; charset=UTF-8";
}, x => {
using(var rs = x.GetRequestStream())
}, x =>
{
using (var rs = x.GetRequestStream())
rs.Write(data, 0, data.Length);
});
m_filecache[newname] = new GoogleDriveFolderItem[] { nf };
m_filecache.Remove(oldname);
}
@@ -397,9 +412,24 @@ namespace Duplicati.Library.Backend.GoogleDrive
public void Dispose()
{
}
#endregion
private NameValueCollection SupportsTeamDriveParam()
{
return m_useTeamDrive ? new NameValueCollection {
{ WebApi.GoogleDrive.QueryParam.SupportsTeamDrive,
WebApi.GoogleDrive.QueryValue.True }
} : null;
}
#endregion
private NameValueCollection IncludeTeamDriveParam()
{
return m_useTeamDrive ? new NameValueCollection {
{ WebApi.GoogleDrive.QueryParam.IncludeTeamDrive,
WebApi.GoogleDrive.QueryValue.True } } : null;
}
private class GoogleDriveParentReference
{
@@ -470,40 +500,52 @@ namespace Duplicati.Library.Backend.GoogleDrive
private IEnumerable<GoogleDriveFolderItem> ListFolder(string parentfolder, bool? onlyFolders = null, string name = null)
{
var p = new string[] {
var fileQuery = new string[] {
string.IsNullOrEmpty(name) ? null : string.Format("title = '{0}'", EscapeTitleEntries(name)),
onlyFolders == null ? null : string.Format("mimeType {0}= '{1}'", onlyFolders.Value ? "" : "!", FOLDER_MIMETYPE),
string.Format("'{0}' in parents", EscapeTitleEntries(parentfolder))
};
var url = string.Format("{0}/files?q={1}{2}", DRIVE_API_URL, Library.Utility.Uri.UrlEncode(string.Join(" and ", p.Where(x => x != null))), m_useTeamDrive ? "&supportsTeamDrives=true&includeTeamDriveItems=true" : string.Empty);
var token = string.Empty;
var queryParams = new NameValueCollection
{
{WebApi.GoogleDrive.QueryParam.File,
Library.Utility.Uri.UrlEncode(string.Join(" and ", fileQuery.Where(x => x != null)))},
};
queryParams.Add(SupportsTeamDriveParam());
queryParams.Add(IncludeTeamDriveParam());
do
while (true)
{
var res = m_oauth.GetJSONData<GoogleDriveListResponse>(url + (string.IsNullOrWhiteSpace(token) ? "" : "&pageToken=" + Library.Utility.Uri.UrlEncode(token)));
var url = WebApi.GoogleDrive.FileQueryUrl(queryParams);
var res = m_oauth.GetJSONData<GoogleDriveListResponse>(url);
foreach (var n in res.items)
yield return n;
token = res.nextPageToken;
} while (!string.IsNullOrWhiteSpace(token));
var token = res.nextPageToken;
if (string.IsNullOrWhiteSpace(token))
break;
queryParams.Set(WebApi.GoogleDrive.QueryParam.PageToken, token);
}
}
private GoogleDriveAboutResponse GetAboutInfo()
{
var url = string.Format("{0}/about", DRIVE_API_URL);
{
var url = Library.Utility.Uri.UriBuilder(WebApi.GoogleDrive.Url.DRIVE, WebApi.GoogleDrive.Path.About);
return m_oauth.GetJSONData<GoogleDriveAboutResponse>(url);
}
private GoogleDriveFolderItem CreateFolder(string name, string parent)
{
var url = string.Format("{0}/files{1}", DRIVE_API_URL, m_useTeamDrive ? "?supportsTeamDrives=true" : string.Empty);
var folder = new GoogleDriveFolderItem() {
var url = WebApi.GoogleDrive.FileQueryUrl(SupportsTeamDriveParam());
var folder = new GoogleDriveFolderItem()
{
title = name,
description = name,
mimeType = FOLDER_MIMETYPE,
labels = new GoogleDriveFolderItemLabels { hidden = true },
parents = new GoogleDriveParentReference[] { new GoogleDriveParentReference() { id = parent } }
parents = new GoogleDriveParentReference[] { new GoogleDriveParentReference { id = parent } }
};
var data = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(folder));
@@ -514,13 +556,11 @@ namespace Duplicati.Library.Backend.GoogleDrive
x.ContentType = "application/json; charset=UTF-8";
x.ContentLength = data.Length;
}, req => {
using(var rs = req.GetRequestStream())
}, req =>
{
using (var rs = req.GetRequestStream())
rs.Write(data, 0, data.Length);
});
}
}
}
@@ -0,0 +1,72 @@
// Copyright (C) 2018, 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.Collections.Specialized;
namespace Duplicati.Library.Backend.WebApi
{
static class GoogleDrive
{
public static class Url
{
public const string DRIVE = "https://www.googleapis.com/drive/v2";
public const string UPLOAD = "https://www.googleapis.com/upload/drive/v2";
}
public static class Path
{
public const string File = "files";
public const string About = "about";
}
public static class QueryParam
{
public const string File = "q";
public const string SupportsTeamDrive = "supportsTeamDrives";
public const string IncludeTeamDrive = "includeTeamDriveItems";
public const string PageToken = "pageToken";
public const string UploadType = "uploadType";
public const string Alt = "alt";
}
public static class QueryValue
{
public const string True = "true";
public const string Resumable = "resumable";
public const string Media = "media";
}
public static string FileQueryUrl(NameValueCollection values)
{
return Library.Utility.Uri.UriBuilder(Url.DRIVE, Path.File, values);
}
public static string FileQueryUrl(string fileId, NameValueCollection values = null)
{
return Library.Utility.Uri.UriBuilder(Url.DRIVE, Library.Utility.Uri.ConcatPaths(Path.File, fileId), values);
}
public static string FileUploadUrl(string fileId, NameValueCollection values)
{
return Library.Utility.Uri.UriBuilder(Url.UPLOAD, Library.Utility.Uri.ConcatPaths(Path.File, fileId), values);
}
public static string FileUploadUrl(NameValueCollection values)
{
return Library.Utility.Uri.UriBuilder(Url.UPLOAD, Path.File, values);
}
}
}
@@ -39,7 +39,7 @@ namespace Duplicati.Library.Backend.HubiC
private class OpenStackHelper : OpenStackStorage
{
private OAuthHelper m_helper;
private readonly OAuthHelper m_helper;
private HubiCAuthResponse m_token;
public OpenStackHelper(string authid, string url)
@@ -44,7 +44,7 @@ namespace Duplicati.Library.Backend
private readonly string m_url_device;
private readonly string m_url;
private readonly string m_url_upload;
private System.Net.NetworkCredential m_userInfo;
private readonly System.Net.NetworkCredential m_userInfo;
private readonly byte[] m_copybuffer = new byte[Duplicati.Library.Utility.Utility.DEFAULT_BUFFER_SIZE];
public Jottacloud()
@@ -24,11 +24,11 @@ namespace Duplicati.Library.Backend.Mega
{
public class MegaBackend: IBackend, IStreamingBackend
{
private string m_username = null;
private string m_password = null;
private readonly string m_username = null;
private readonly string m_password = null;
private Dictionary<string, List<INode>> m_filecache;
private INode m_currentFolder = null;
private string m_prefix = null;
private readonly string m_prefix = null;
private MegaApiClient m_client;
@@ -140,9 +140,9 @@ namespace Duplicati.Library
/// <returns>The deserialized JSON data.</returns>
/// <param name="url">The remote URL</param>
/// <param name="setup">A callback method that can be used to customize the request, e.g. by setting the method, content-type and headers.</param>
/// <param name="setupreq">A callback method that can be used to submit data into the body of the request.</param>
/// <param name="setupbodyreq">A callback method that can be used to submit data into the body of the request.</param>
/// <typeparam name="T">The type of data to return.</typeparam>
public virtual T GetJSONData<T>(string url, Action<HttpWebRequest> setup = null, Action<AsyncHttpRequest> setupreq = null)
public virtual T GetJSONData<T>(string url, Action<HttpWebRequest> setup = null, Action<AsyncHttpRequest> setupbodyreq = null)
{
var req = CreateRequest(url);
@@ -151,8 +151,8 @@ namespace Duplicati.Library
var areq = new AsyncHttpRequest(req);
if (setupreq != null)
setupreq(areq);
if (setupbodyreq != null)
setupbodyreq(areq);
return ReadJSONResponse<T>(areq);
}
@@ -28,14 +28,14 @@ namespace Duplicati.Library.Backend
private static readonly string USER_AGENT = string.Format("Duplicati v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
private string m_rootfolder;
private string m_prefix;
private readonly string m_rootfolder;
private readonly string m_prefix;
private string m_userid;
private WLID_FolderItem m_folderid;
private OAuthHelper m_oauth;
private readonly OAuthHelper m_oauth;
private Dictionary<string, string> m_fileidCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, string> m_fileidCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private readonly byte[] m_copybuffer = new byte[Duplicati.Library.Utility.Utility.DEFAULT_BUFFER_SIZE];
@@ -14,18 +14,65 @@
// 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;
using System;
using Duplicati.Library.Interface;
using System.Collections.Generic;
using System.Linq;
namespace Duplicati.Library.Backend.OpenStack
{
public class SwiftConfig : IWebModule
namespace Duplicati.Library.Backend.OpenStack
{
{
private const ConfigType DEFAULT_CONFIG_TYPE = ConfigType.Providers;
private static readonly string DEFAULT_CONFIG_TYPE_STR = Enum.GetName(typeof(ConfigType), DEFAULT_CONFIG_TYPE);
private const string KEY_CONFIGTYPE = "openstack-config";
public enum ConfigType
{
Providers,
Versions,
}
public SwiftConfig()
{
private const ConfigType DEFAULT_CONFIG_TYPE = ConfigType.Providers;
}
public System.Collections.Generic.IDictionary<string, string> Execute(System.Collections.Generic.IDictionary<string, string> options)
{
string k;
options.TryGetValue(KEY_CONFIGTYPE, out k);
if (string.IsNullOrWhiteSpace(k))
k = DEFAULT_CONFIG_TYPE_STR;
ConfigType ct;
if (!Enum.TryParse<ConfigType>(k, true, out ct))
ct = DEFAULT_CONFIG_TYPE;
switch (ct)
{
case ConfigType.Versions:
private const string KEY_CONFIGTYPE = "openstack-config";
return OpenStack.OpenStackStorage.OPENSTACK_VERSIONS.ToDictionary((x) => x.Key, (y) => y.Value);
default:
return OpenStack.OpenStackStorage.KNOWN_OPENSTACK_PROVIDERS.ToDictionary((x) => x.Key, (y) => y.Value);
}
}
public string Key { get { return "openstack-getconfig"; } }
public string DisplayName { get { return "OpenStack configuration module"; } }
public string Description { get { return "Exposes OpenStack configuration as a web module"; } }
public System.Collections.Generic.IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, "The config to get", "Provides different config values", DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
});
}
}
}
}
@@ -64,6 +64,7 @@ namespace Duplicati.Library.Backend.OpenStack
new KeyValuePair<string, string>("Rackspace UK", "https://lon.identity.api.rackspacecloud.com/v2.0"),
new KeyValuePair<string, string>("OVH Cloud Storage", "https://auth.cloud.ovh.net/v2.0"),
new KeyValuePair<string, string>("Selectel Cloud Storage", "https://auth.selcdn.ru"),
new KeyValuePair<string, string>("Memset Cloud Storage", "https://auth.storage.memset.com"),
};
public static readonly KeyValuePair<string, string>[] OPENSTACK_VERSIONS = {
+22 -4
View File
@@ -13,15 +13,33 @@
//
// 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
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using Duplicati.Library.Localization.Short;
namespace Duplicati.Library.Backend.Strings
{
namespace Duplicati.Library.Backend.Strings
internal static class OpenStack {
public static string Description { get { return LC.L(@"This backend can read and write data to Swift (OpenStack Object Storage). Supported format is ""openstack://container/folder""."); } }
public static string DisplayName { get { return LC.L(@"OpenStack Simple Storage"); } }
public static string MissingOptionError(string optionname) { return LC.L(@"Missing required option: {0}", optionname); }
public static string PasswordOptionLong(string tenantnameoption) { return LC.L(@"The password used to connect to the server. This may also be supplied as the environment variable ""AUTH_PASSWORD"". If the password is supplied, --{0} must also be set", tenantnameoption); }
public static string PasswordOptionShort { get { return LC.L(@"Supplies the password used to connect to the server"); } }
public static string DomainnameOptionLong { get { return LC.L(@"The domain name of the user used to connect to the server."); } }
public static string DomainnameOptionShort { get { return LC.L(@"Supplies the domain used to connect to the server"); } }
public static string UsernameOptionLong { 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 UsernameOptionShort { get { return LC.L(@"Supplies the username used to connect to the server"); } }
public static string DisplayName { get { return LC.L(@"OpenStack Simple Storage"); } }
public static string TenantnameOptionLong { get { return LC.L(@"The Tenant Name is commonly the paying user account name. This option must be supplied when authenticating with a password, but is not required when using an API key."); } }
public static string TenantnameOptionShort { get { return LC.L(@"Supplies the Tenant Name used to connect to the server"); } }
public static string ApikeyOptionLong { get { return LC.L(@"The API key can be used to connect without supplying a password and tenant ID with some providers."); } }
public static string ApikeyOptionShort { get { return LC.L(@"Supplies the API key used to connect to the server"); } }
public static string AuthuriOptionLong(string providers) { return LC.L(@"The authentication URL is used to authenticate the user and find the storage service. The URL commonly ends with ""/v2.0"". Known providers are: {0}{1}", System.Environment.NewLine, providers); }
public static string AuthuriOptionShort { get { return LC.L(@"Supplies the authentication URL"); } }
public static string VersionOptionLong { get { return LC.L(@"The keystone API version to use, valid values are 'v2' and 'v3'."); } }
public static string VersionOptionShort { get { return LC.L(@"The keystone API version to use"); } }
public static string PasswordOptionLong(string tenantnameoption) { return LC.L(@"The password used to connect to the server. This may also be supplied as the environment variable ""AUTH_PASSWORD"". If the password is supplied, --{0} must also be set", tenantnameoption); }
public static string RegionOptionLong { get { return LC.L(@"This option is only used when creating a container, and is used to indicate where the container should be placed. Consult your provider for a list of valid regions, or leave empty for the default region."); } }
public static string RegionOptionShort { get { return LC.L(@"Supplies the region used for creating a container"); } }
}
}
+315 -312
View File
@@ -1,316 +1,319 @@
#region Disclaimer / License
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;
using Duplicati.Library.Interface;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Globalization;
using System.IO;
namespace Duplicati.Library.Backend
{
public class Rclone : IBackend
{
private const string OPTION_LOCAL_REPO = "rclone-local-repository";
private const string OPTION_REMOTE_REPO = "rclone-remote-repository";
private const string OPTION_REMOTE_PATH = "rclone-remote-path";
private const string OPTION_RCLONE = "rclone-option";
private const string OPTION_RCLONE_EXECUTABLE = "rclone-executable";
private const string RCLONE_ERROR_DIRECTORY_NOT_FOUND = "directory not found";
private const string RCLONE_ERROR_CONFIG_NOT_FOUND = "didn't find section in config file";
private string local_repo;
private string remote_repo;
private string remote_path;
private string opt_rclone;
private string rclone_executable;
public Rclone()
{
}
public Rclone(string url, Dictionary<string, string> options)
{
var uri = new Utility.Uri(url);
remote_repo = uri.Host;
remote_path = uri.Path;
local_repo = "local";
opt_rclone = "";
rclone_executable = "rclone";
/*should check here if program is installed */
if (options.ContainsKey(OPTION_LOCAL_REPO))
local_repo = options[OPTION_LOCAL_REPO];
if (options.ContainsKey(OPTION_REMOTE_REPO))
remote_repo = options[OPTION_REMOTE_REPO];
if (options.ContainsKey(OPTION_REMOTE_PATH))
remote_path = options[OPTION_REMOTE_PATH];
if (options.ContainsKey(OPTION_RCLONE))
opt_rclone = options[OPTION_RCLONE];
if (options.ContainsKey(OPTION_RCLONE_EXECUTABLE))
rclone_executable = options[OPTION_RCLONE_EXECUTABLE];
#if DEBUG
Console.WriteLine(string.Format("Constructor {0}: {1}:{2} {3}", local_repo, remote_repo, remote_path, opt_rclone));
#endif
}
#region IBackendInterface Members
public string DisplayName
{
get { return Strings.Rclone.DisplayName; }
}
public string ProtocolKey
{
get { return "rclone"; }
}
public bool SupportsStreaming
{
get { return false; }
}
private string RcloneCommandExecuter(String command, String arguments)
{
StringBuilder outputBuilder = new StringBuilder();
StringBuilder errorBuilder = new StringBuilder();
Process process;
ProcessStartInfo psi = new ProcessStartInfo
{
Arguments = String.Format("{0} {1}", arguments, opt_rclone),
CreateNoWindow = true,
FileName = command,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
};
#if DEBUG
Console.Error.WriteLine(String.Format("command executing: {0} {1}", psi.FileName, psi.Arguments));
#endif
process = new Process
{
StartInfo = psi,
// enable raising events because Process does not raise events by default
EnableRaisingEvents = true
};
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler
(
delegate (object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data))
{
#if DEBUG
// Console.Error.WriteLine(String.Format("output {0}", e.Data));
#endif
// append the new data to the data already read-in
outputBuilder.Append(e.Data);
};
}
);
process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler
(
delegate (object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data))
{
#if DEBUG
Console.Error.WriteLine(String.Format("error {0}", e.Data));
#endif
errorBuilder.Append(e.Data);
}
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
process.CancelOutputRead();
process.CancelErrorRead();
if (errorBuilder.ToString().Contains(RCLONE_ERROR_DIRECTORY_NOT_FOUND))
{
throw new FolderMissingException(errorBuilder.ToString());
}
if (errorBuilder.ToString().Contains(RCLONE_ERROR_CONFIG_NOT_FOUND))
{
throw new Exception(String.Format("Missing config file? {0}", errorBuilder.ToString()));
}
if (errorBuilder.Length > 0) {
throw new Exception(errorBuilder.ToString());
}
Console.Error.WriteLine(errorBuilder.ToString());
return outputBuilder.ToString();
}
public IEnumerable<IFileEntry> List()
{
JArray files;
String str_result;
try
{
str_result = RcloneCommandExecuter(rclone_executable, String.Format("lsjson {0}:{1}", remote_repo, remote_path));
// this will give an error if the executable does not exist.
}
catch (FolderMissingException ex)
{
throw new FolderMissingException(ex);
}
using (JsonReader jsonReader = new JsonTextReader(new StringReader(str_result)))
{
//no date parsing by JArray needed, will be parsed later
jsonReader.DateParseHandling = DateParseHandling.None;
var array = JArray.Load(jsonReader);
foreach (JObject item in array)
{
#if DEBUG
Console.Error.WriteLine(item.ToString());
#endif
FileEntry fe = new FileEntry(
item.GetValue("Name").Value<string>(),
item.GetValue("Size").Value<long>(),
DateTime.Parse(item.GetValue("ModTime").Value<string>()),
DateTime.Parse(item.GetValue("ModTime").Value<string>())
)
{
IsFolder = item.GetValue("IsDir").Value<bool>()
};
yield return fe;
}
}
}
public void Put(string remotename, string filename)
{
try
{
RcloneCommandExecuter(rclone_executable, String.Format("copyto {0}:{1} {2}:{3}/{4}", local_repo, filename, remote_repo, remote_path, remotename));
}
catch (FolderMissingException ex)
{
throw new FileMissingException(ex);
}
}
public void Get(string remotename, string filename)
{
try
{
RcloneCommandExecuter(rclone_executable, String.Format("copyto {2}:{3}/{4} {0}:{1}", local_repo, filename, remote_repo, remote_path, remotename));
}
catch (FolderMissingException ex) {
throw new FileMissingException(ex);
}
}
public void Delete(string remotename)
{
//this will actually delete the folder if remotename is a folder...
// Will give a "directory not found" error if the file does not exist, need to change that to a missing file exception
try
{
RcloneCommandExecuter(rclone_executable, String.Format("delete {0}:{1}/{2}", remote_repo, remote_path, remotename));
}
catch (FolderMissingException ex) {
throw new FileMissingException(ex);
}
}
public IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(OPTION_LOCAL_REPO, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneLocalRepoShort, Strings.Rclone.RcloneLocalRepoLong, "local"),
new CommandLineArgument(OPTION_REMOTE_REPO, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneRemoteRepoShort, Strings.Rclone.RcloneRemoteRepoLong, "remote"),
new CommandLineArgument(OPTION_REMOTE_PATH, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneRemotePathShort, Strings.Rclone.RcloneRemotePathLong, "backup"),
new CommandLineArgument(OPTION_RCLONE, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneOptionRcloneShort, Strings.Rclone.RcloneOptionRcloneLong, ""),
new CommandLineArgument(OPTION_RCLONE_EXECUTABLE, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneExecutableShort, Strings.Rclone.RcloneExecutableLong, "rclone")
});
}
}
public string Description
{
get
{
return Strings.Rclone.Description;
}
#region Disclaimer / License
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;
using Duplicati.Library.Interface;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Globalization;
using System.IO;
namespace Duplicati.Library.Backend
{
public class Rclone : IBackend
{
private const string OPTION_LOCAL_REPO = "rclone-local-repository";
private const string OPTION_REMOTE_REPO = "rclone-remote-repository";
private const string OPTION_REMOTE_PATH = "rclone-remote-path";
private const string OPTION_RCLONE = "rclone-option";
private const string OPTION_RCLONE_EXECUTABLE = "rclone-executable";
private const string RCLONE_ERROR_DIRECTORY_NOT_FOUND = "directory not found";
private const string RCLONE_ERROR_CONFIG_NOT_FOUND = "didn't find section in config file";
private readonly string local_repo;
private readonly string remote_repo;
private readonly string remote_path;
private readonly string opt_rclone;
private readonly string rclone_executable;
public Rclone()
{
}
public string[] DNSName
{
get { return new string[] { remote_repo }; }
public Rclone(string url, Dictionary<string, string> options)
{
var uri = new Utility.Uri(url);
remote_repo = uri.Host;
remote_path = uri.Path;
local_repo = "local";
opt_rclone = "";
rclone_executable = "rclone";
/*should check here if program is installed */
if (options.ContainsKey(OPTION_LOCAL_REPO))
local_repo = options[OPTION_LOCAL_REPO];
if (options.ContainsKey(OPTION_REMOTE_REPO))
remote_repo = options[OPTION_REMOTE_REPO];
if (options.ContainsKey(OPTION_REMOTE_PATH))
remote_path = options[OPTION_REMOTE_PATH];
if (options.ContainsKey(OPTION_RCLONE))
opt_rclone = options[OPTION_RCLONE];
if (options.ContainsKey(OPTION_RCLONE_EXECUTABLE))
rclone_executable = options[OPTION_RCLONE_EXECUTABLE];
#if DEBUG
Console.WriteLine(string.Format("Constructor {0}: {1}:{2} {3}", local_repo, remote_repo, remote_path, opt_rclone));
#endif
}
public void Test()
{
this.TestList();
}
public void CreateFolder()
{
RcloneCommandExecuter(rclone_executable, String.Format("mkdir {0}:{1}", remote_repo, remote_path));
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
}
}
#region IBackendInterface Members
public string DisplayName
{
get { return Strings.Rclone.DisplayName; }
}
public string ProtocolKey
{
get { return "rclone"; }
}
public bool SupportsStreaming
{
get { return false; }
}
private string RcloneCommandExecuter(String command, String arguments)
{
StringBuilder outputBuilder = new StringBuilder();
StringBuilder errorBuilder = new StringBuilder();
Process process;
ProcessStartInfo psi = new ProcessStartInfo
{
Arguments = String.Format("{0} {1}", arguments, opt_rclone),
CreateNoWindow = true,
FileName = command,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
};
#if DEBUG
Console.Error.WriteLine(String.Format("command executing: {0} {1}", psi.FileName, psi.Arguments));
#endif
process = new Process
{
StartInfo = psi,
// enable raising events because Process does not raise events by default
EnableRaisingEvents = true
};
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler
(
delegate (object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data))
{
#if DEBUG
// Console.Error.WriteLine(String.Format("output {0}", e.Data));
#endif
// append the new data to the data already read-in
outputBuilder.Append(e.Data);
};
}
);
process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler
(
delegate (object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data))
{
#if DEBUG
Console.Error.WriteLine(String.Format("error {0}", e.Data));
#endif
errorBuilder.Append(e.Data);
}
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
process.CancelOutputRead();
process.CancelErrorRead();
if (errorBuilder.ToString().Contains(RCLONE_ERROR_DIRECTORY_NOT_FOUND))
{
throw new FolderMissingException(errorBuilder.ToString());
}
if (errorBuilder.ToString().Contains(RCLONE_ERROR_CONFIG_NOT_FOUND))
{
throw new Exception(String.Format("Missing config file? {0}", errorBuilder.ToString()));
}
if (errorBuilder.Length > 0) {
throw new Exception(errorBuilder.ToString());
}
Console.Error.WriteLine(errorBuilder.ToString());
return outputBuilder.ToString();
}
public IEnumerable<IFileEntry> List()
{
String str_result;
try
{
str_result = RcloneCommandExecuter(rclone_executable, String.Format("lsjson {0}:{1}", remote_repo, remote_path));
// this will give an error if the executable does not exist.
}
catch (FolderMissingException ex)
{
throw new FolderMissingException(ex);
}
using (JsonReader jsonReader = new JsonTextReader(new StringReader(str_result)))
{
//no date parsing by JArray needed, will be parsed later
jsonReader.DateParseHandling = DateParseHandling.None;
var array = JArray.Load(jsonReader);
foreach (JObject item in array)
{
#if DEBUG
Console.Error.WriteLine(item.ToString());
#endif
FileEntry fe = new FileEntry(
item.GetValue("Name").Value<string>(),
item.GetValue("Size").Value<long>(),
DateTime.Parse(item.GetValue("ModTime").Value<string>()),
DateTime.Parse(item.GetValue("ModTime").Value<string>())
)
{
IsFolder = item.GetValue("IsDir").Value<bool>()
};
yield return fe;
}
}
}
public void Put(string remotename, string filename)
{
try
{
RcloneCommandExecuter(rclone_executable, String.Format("copyto {0}:{1} {2}:{3}/{4}", local_repo, filename, remote_repo, remote_path, remotename));
}
catch (FolderMissingException ex)
{
throw new FileMissingException(ex);
}
}
public void Get(string remotename, string filename)
{
try
{
RcloneCommandExecuter(rclone_executable, String.Format("copyto {2}:{3}/{4} {0}:{1}", local_repo, filename, remote_repo, remote_path, remotename));
}
catch (FolderMissingException ex) {
throw new FileMissingException(ex);
}
}
public void Delete(string remotename)
{
//this will actually delete the folder if remotename is a folder...
// Will give a "directory not found" error if the file does not exist, need to change that to a missing file exception
try
{
RcloneCommandExecuter(rclone_executable, String.Format("delete {0}:{1}/{2}", remote_repo, remote_path, remotename));
}
catch (FolderMissingException ex) {
throw new FileMissingException(ex);
}
}
public IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(OPTION_LOCAL_REPO, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneLocalRepoShort, Strings.Rclone.RcloneLocalRepoLong, "local"),
new CommandLineArgument(OPTION_REMOTE_REPO, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneRemoteRepoShort, Strings.Rclone.RcloneRemoteRepoLong, "remote"),
new CommandLineArgument(OPTION_REMOTE_PATH, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneRemotePathShort, Strings.Rclone.RcloneRemotePathLong, "backup"),
new CommandLineArgument(OPTION_RCLONE, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneOptionRcloneShort, Strings.Rclone.RcloneOptionRcloneLong, ""),
new CommandLineArgument(OPTION_RCLONE_EXECUTABLE, CommandLineArgument.ArgumentType.String, Strings.Rclone.RcloneExecutableShort, Strings.Rclone.RcloneExecutableLong, "rclone")
});
}
}
public string Description
{
get
{
return Strings.Rclone.Description;
}
}
public string[] DNSName
{
get { return new string[] { remote_repo }; }
}
public void Test()
{
this.TestList();
}
public void CreateFolder()
{
RcloneCommandExecuter(rclone_executable, String.Format("mkdir {0}:{1}", remote_repo, remote_path));
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
}
}
+2 -2
View File
@@ -126,8 +126,8 @@ namespace Duplicati.Library.Backend
}
}
private string m_bucket;
private string m_prefix;
private readonly string m_bucket;
private readonly string m_prefix;
public const string DEFAULT_S3_HOST = "s3.amazonaws.com";
public const string S3_EU_REGION_NAME = "eu-west-1";
+10 -10
View File
@@ -37,18 +37,18 @@ namespace Duplicati.Library.Backend
public const string SSH_TIMEOUT_OPTION = "ssh-operation-timeout";
public const string SSH_KEEPALIVE_OPTION = "ssh-keepalive";
Dictionary<string, string> m_options;
readonly Dictionary<string, string> m_options;
private string m_server;
private string m_path;
private string m_username;
private string m_password;
private string m_fingerprint;
private bool m_fingerprintallowall;
private TimeSpan m_operationtimeout;
private TimeSpan m_keepaliveinterval;
private readonly string m_server;
private readonly string m_path;
private readonly string m_username;
private readonly string m_password;
private readonly string m_fingerprint;
private readonly bool m_fingerprintallowall;
private readonly TimeSpan m_operationtimeout;
private readonly TimeSpan m_keepaliveinterval;
private int m_port = 22;
private readonly int m_port = 22;
private SftpClient m_con;
@@ -54,15 +54,15 @@ namespace Duplicati.Library.Backend
#region [Variables and constants declarations]
/// <summary> Auth-stripped HTTPS-URI as passed to constructor. </summary>
private Utility.Uri m_orgUrl;
private readonly Utility.Uri m_orgUrl;
/// <summary> Server relative path to backup folder. </summary>
private string m_serverRelPath;
private readonly string m_serverRelPath;
/// <summary> User's credentials to create client context </summary>
private System.Net.ICredentials m_userInfo;
/// <summary> Flag indicating to move files to recycler on deletion. </summary>
private bool m_deleteToRecycler = false;
private readonly bool m_deleteToRecycler = false;
/// <summary> Flag indicating to use UploadBinaryDirect. </summary>
private bool m_useBinaryDirectMode = false;
private readonly bool m_useBinaryDirectMode = false;
/// <summary> URL to SharePoint web. Will be determined from m_orgUri on first use. </summary>
private string m_spWebUrl;
@@ -70,10 +70,10 @@ namespace Duplicati.Library.Backend
private SP.ClientContext m_spContext;
/// <summary> The chunk size for uploading files. </summary>
private int m_fileChunkSize = 4 << 20; // Default: 4MB
private readonly int m_fileChunkSize = 4 << 20; // Default: 4MB
/// <summary> The chunk size for uploading files. </summary>
private int m_useContextTimeoutMs = -1; // default: do not touch original setting
private readonly int m_useContextTimeoutMs = -1; // default: do not touch original setting
#endregion
@@ -666,12 +666,14 @@ namespace Duplicati.Library.Backend
string[] folderNames = m_serverRelPath.Substring(0, m_serverRelPath.Length - 1).Split('/');
folderNames = Array.ConvertAll(folderNames, fold => System.Net.WebUtility.UrlDecode(fold));
var spfolders = new SP.Folder[folderNames.Length];
string folderRelPath = "";
StringBuilder relativePathBuilder = new StringBuilder();
int fi = 0;
for (; fi < folderNames.Length; fi++)
{
folderRelPath += System.Web.HttpUtility.UrlPathEncode(folderNames[fi]) + "/";
if (fi < pathLengthToWeb) continue;
relativePathBuilder.Append(System.Web.HttpUtility.UrlPathEncode(folderNames[fi])).Append("/");
if (fi < pathLengthToWeb) continue;
string folderRelPath = relativePathBuilder.ToString();
var folder = ctx.Web.GetFolderByServerRelativeUrl(folderRelPath);
spfolders[fi] = folder;
ctx.Load(folder, f => f.Exists);
+5 -5
View File
@@ -15,11 +15,11 @@ namespace Duplicati.Library.Backend.Sia
private const string SIA_TARGETPATH = "sia-targetpath";
private const string SIA_REDUNDANCY = "sia-redundancy";
private string m_apihost;
private int m_apiport;
private string m_targetpath;
private float m_redundancy;
private System.Net.NetworkCredential m_user;
private readonly string m_apihost;
private readonly int m_apiport;
private readonly string m_targetpath;
private readonly float m_redundancy;
private readonly System.Net.NetworkCredential m_user;
public Sia() {
@@ -29,8 +29,8 @@ namespace Duplicati.Library.Backend
{
public class TahoeBackend : IBackend, IStreamingBackend
{
private string m_url;
private bool m_useSSL = false;
private readonly string m_url;
private readonly bool m_useSSL = false;
private readonly byte[] m_copybuffer = new byte[Duplicati.Library.Utility.Utility.DEFAULT_BUFFER_SIZE];
private class TahoeEl
+12 -12
View File
@@ -26,18 +26,18 @@ namespace Duplicati.Library.Backend
{
public class WEBDAV : IBackend, IStreamingBackend
{
private System.Net.NetworkCredential m_userInfo;
private string m_url;
private string m_path;
private string m_sanitizedUrl;
private string m_reverseProtocolUrl;
private string m_rawurl;
private string m_rawurlPort;
private string m_dnsName;
private bool m_useIntegratedAuthentication = false;
private bool m_forceDigestAuthentication = false;
private bool m_useSSL = false;
private string m_debugPropfindFile = null;
private readonly System.Net.NetworkCredential m_userInfo;
private readonly string m_url;
private readonly string m_path;
private readonly string m_sanitizedUrl;
private readonly string m_reverseProtocolUrl;
private readonly string m_rawurl;
private readonly string m_rawurlPort;
private readonly string m_dnsName;
private readonly bool m_useIntegratedAuthentication = false;
private readonly bool m_forceDigestAuthentication = false;
private readonly bool m_useSSL = false;
private readonly string m_debugPropfindFile = null;
private readonly byte[] m_copybuffer = new byte[Duplicati.Library.Utility.Utility.DEFAULT_BUFFER_SIZE];
/// <summary>
@@ -92,7 +92,7 @@ namespace Duplicati.Library.Compression
/// <summary>
/// This property indicates reading or writing access mode of the file archive.
/// </summary>
ArchiveMode m_mode;
readonly ArchiveMode m_mode;
/// <summary>
/// Gets the number of bytes expected to be written after the stream is disposed
@@ -126,17 +126,17 @@ namespace Duplicati.Library.Compression
/// <summary>
/// The compression level applied when the hint does not indicate incompressible
/// </summary>
private SharpCompress.Compressors.Deflate.CompressionLevel m_defaultCompressionLevel;
private readonly SharpCompress.Compressors.Deflate.CompressionLevel m_defaultCompressionLevel;
/// <summary>
/// The compression level applied when the hint does not indicate incompressible
/// </summary>
private CompressionType m_compressionType;
private readonly CompressionType m_compressionType;
/// <summary>
/// A flag indicating if zip64 is in use
/// </summary>
private bool m_usingZip64;
private readonly bool m_usingZip64;
/// <summary>
/// Default constructor, used to read file extension and supported commands
@@ -306,9 +306,9 @@ namespace Duplicati.Library.Compression
var q = m_entryDict.Values.AsEnumerable();
if (!string.IsNullOrEmpty(prefix))
q = q.Where(x =>
x.Key.StartsWith(prefix, Duplicati.Library.Utility.Utility.ClientFilenameStringComparision)
x.Key.StartsWith(prefix, Duplicati.Library.Utility.Utility.ClientFilenameStringComparison)
||
x.Key.Replace('\\', '/').StartsWith(prefix, Duplicati.Library.Utility.Utility.ClientFilenameStringComparision)
x.Key.Replace('\\', '/').StartsWith(prefix, Duplicati.Library.Utility.Utility.ClientFilenameStringComparison)
);
return q.Select(x => new KeyValuePair<string, long>(x.Key, x.Size)).ToArray();
@@ -174,7 +174,7 @@ namespace Duplicati.Library.Compression
if (m_reader == null)
throw new InvalidOperationException(Strings.SevenZipCompression.NoReaderError);
StringComparison sc = Library.Utility.Utility.ClientFilenameStringComparision;
StringComparison sc = Library.Utility.Utility.ClientFilenameStringComparison;
return
from n in m_reader.GetFiles(m_archive)
@@ -222,8 +222,8 @@ namespace Duplicati.Library.Compression
private sealed class WriterEntry : ManagedLzma.LZMA.Master.SevenZip.IArchiveWriterEntry
{
private string mName;
private DateTime? mTimestamp;
private readonly string mName;
private readonly DateTime? mTimestamp;
internal WriterEntry(string name, DateTime? timestamp)
{
@@ -138,10 +138,10 @@ namespace Duplicati.Library.DynamicLoader
/// <summary>
/// The static instance used to access backend information
/// </summary>
private static BackendLoaderSub _backendLoader = new BackendLoaderSub();
private static readonly BackendLoaderSub _backendLoader = new BackendLoaderSub();
#region Public static API
/// <summary>
/// Gets a list of loaded backends, the instances can be used to extract interface information, not used to interact with the backend.
/// </summary>
@@ -103,7 +103,7 @@ namespace Duplicati.Library.DynamicLoader
/// <summary>
/// The static instance used to access compression module information
/// </summary>
private static CompressionLoaderSub _compressionLoader = new CompressionLoaderSub();
private static readonly CompressionLoaderSub _compressionLoader = new CompressionLoaderSub();
#region Public static API
@@ -104,7 +104,7 @@ namespace Duplicati.Library.DynamicLoader
/// <summary>
/// The static instance used to access encryption module information
/// </summary>
private static EncryptionLoaderSub _encryptionLoader = new EncryptionLoaderSub();
private static readonly EncryptionLoaderSub _encryptionLoader = new EncryptionLoaderSub();
#region Public static API
@@ -94,17 +94,17 @@ namespace Duplicati.Library.Encryption
/// <summary>
/// The PGP program to use, should be with absolute path
/// </summary>
private string m_programpath = "gpg";
private readonly string m_programpath = "gpg";
/// <summary>
/// Commandline switches for encryption
/// </summary>
private string m_encryption_args;
private readonly string m_encryption_args;
/// <summary>
/// Commandline switches for decryption
/// </summary>
private string m_decryption_args;
private readonly string m_decryption_args;
/// <summary>
/// The key used for cryptographic operations
@@ -25,7 +25,7 @@ namespace Duplicati.Library.Localization
/// <summary>
/// The previous context
/// </summary>
private object m_prev;
private readonly object m_prev;
/// <summary>
/// Flag to prevent double dispose
@@ -110,7 +110,7 @@ namespace Duplicati.Library.Localization
/// </summary>
public static ILocalizationService Get(CultureInfo ci)
{
if (ci == CultureInfo.InvariantCulture)
if (ci.Equals(CultureInfo.InvariantCulture))
return InvariantService;
ILocalizationService service;
@@ -33,7 +33,7 @@ namespace Duplicati.Library.Localization
/// <summary>
/// The catalog containing the translations
/// </summary>
private ICatalog catalog = new Catalog();
private readonly ICatalog catalog = new Catalog();
/// <summary>
/// The environment variable used to locate MO files
+2 -2
View File
@@ -27,7 +27,7 @@ namespace Duplicati.Library.Localization.Short
/// <summary>
/// The instance for translation
/// </summary>
private static ILocalizationService LS = LocalizationService.Invariant;
private static readonly ILocalizationService LS = LocalizationService.Invariant;
/// <summary>
/// Localizes the string similar to how string.Format works
@@ -172,7 +172,7 @@ namespace Duplicati.Library.Localization.Short
/// <summary>
/// The instance for translation
/// </summary>
private static ILocalizationService LS = LocalizationService.Installed;
private static readonly ILocalizationService LS = LocalizationService.Installed;
/// <summary>
/// Localizes the string similar to how string.Format works
+1 -1
View File
@@ -33,7 +33,7 @@ namespace Duplicati.Library.Logging
/// </summary>
private static readonly string LOGTAG = "Timer";
private DateTime m_begin;
private readonly DateTime m_begin;
private string m_operation;
private readonly string m_logtag;
private readonly string m_logid;
+5 -5
View File
@@ -17,7 +17,7 @@ namespace Duplicati.Library.Main
{
private class AsyncDownloadedFile : IAsyncDownloadedFile
{
private Exception m_exception;
private readonly Exception m_exception;
private Library.Utility.TempFile m_file;
public string Name { get; private set; }
@@ -53,9 +53,9 @@ namespace Duplicati.Library.Main
}
}
private IList<IRemoteVolume> m_volumes;
private readonly IList<IRemoteVolume> m_volumes;
private BackendManager.IDownloadWaitHandle m_handle;
private BackendManager m_backend;
private readonly BackendManager m_backend;
private int m_index;
private AsyncDownloadedFile m_current;
@@ -129,8 +129,8 @@ namespace Duplicati.Library.Main
}
}
private IList<IRemoteVolume> m_volumes;
private BackendManager m_backend;
private readonly IList<IRemoteVolume> m_volumes;
private readonly BackendManager m_backend;
public AsyncDownloader(IList<IRemoteVolume> volumes, BackendManager backend)
{
+9 -9
View File
@@ -126,7 +126,7 @@ namespace Duplicati.Library.Main
/// <summary>
/// The event that is signaled once the operation is complete or has failed
/// </summary>
private System.Threading.ManualResetEvent DoneEvent;
private readonly System.Threading.ManualResetEvent DoneEvent;
public FileEntryItem(OperationType operation, string remotefilename, Tuple<IndexVolumeWriter, FileEntryItem> indexfile = null)
{
@@ -245,10 +245,10 @@ namespace Duplicati.Library.Main
private class DatabaseCollector
{
private readonly object m_dbqueuelock = new object();
private LocalDatabase m_database;
private System.Threading.Thread m_callerThread;
private readonly LocalDatabase m_database;
private readonly System.Threading.Thread m_callerThread;
private List<IDbEntry> m_dbqueue;
private IBackendWriter m_stats;
private readonly IBackendWriter m_stats;
private interface IDbEntry { }
@@ -349,15 +349,15 @@ namespace Duplicati.Library.Main
}
private readonly BlockingQueue<FileEntryItem> m_queue;
private Options m_options;
private readonly Options m_options;
private volatile Exception m_lastException;
private Library.Interface.IEncryption m_encryption;
private readonly Library.Interface.IEncryption m_encryption;
private readonly object m_encryptionLock = new object();
private Library.Interface.IBackend m_backend;
private string m_backendurl;
private IBackendWriter m_statwriter;
private readonly string m_backendurl;
private readonly IBackendWriter m_statwriter;
private System.Threading.Thread m_thread;
private BasicResults m_taskControl;
private readonly BasicResults m_taskControl;
private readonly DatabaseCollector m_db;
// Cache these
+3 -3
View File
@@ -40,7 +40,7 @@ namespace Duplicati.Library.Main
/// <summary>
/// The parsed type-safe version of the commandline options
/// </summary>
private Options m_options;
private readonly Options m_options;
/// <summary>
/// The destination for all output messages during execution
/// </summary>
@@ -59,7 +59,7 @@ namespace Duplicati.Library.Main
/// <summary>
/// Holds various keys that need to be reset after running the task
/// </summary>
private Dictionary<string, string> m_resetKeys = new Dictionary<string, string>();
private readonly Dictionary<string, string> m_resetKeys = new Dictionary<string, string>();
/// <summary>
/// The thread priority to reset to
@@ -222,7 +222,7 @@ namespace Duplicati.Library.Main
//Sanity check for multiple inclusions of the same folder
for (int i = 0; i < sources.Count; i++)
for (int j = 0; j < sources.Count; j++)
if (i != j && sources[i].StartsWith(sources[j], Library.Utility.Utility.ClientFilenameStringComparision) && sources[i].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), Library.Utility.Utility.ClientFilenameStringComparision))
if (i != j && sources[i].StartsWith(sources[j], Library.Utility.Utility.ClientFilenameStringComparison) && sources[i].EndsWith(System.IO.Path.DirectorySeparatorChar.ToString(), Library.Utility.Utility.ClientFilenameStringComparison))
{
if (filter != null)
{
@@ -28,7 +28,7 @@ namespace Duplicati.Library.Main
/// <summary>
/// The list of log targets to handle
/// </summary>
private List<Tuple<ILogDestination, LogMessageType, Library.Utility.IFilter>> m_targets = new List<Tuple<ILogDestination, LogMessageType, Library.Utility.IFilter>>();
private readonly List<Tuple<ILogDestination, LogMessageType, Library.Utility.IFilter>> m_targets = new List<Tuple<ILogDestination, LogMessageType, Library.Utility.IFilter>>();
/// <summary>
/// Initializes a new instance of the <see cref="T:Duplicati.Library.Main.ControllerMultiLogTarget"/> class.
@@ -28,7 +28,7 @@ namespace Duplicati.Library.Main
/// <summary>
/// The lookup table
/// </summary>
private SortedList<string, T>[] m_lookup;
private readonly SortedList<string, T>[] m_lookup;
/// <summary>
/// The number of entries in the table
/// </summary>
@@ -211,7 +211,7 @@ namespace Duplicati.Library.Main.Database
public Tuple<string, object[]> GetFilelistWhereClause(DateTime time, long[] versions, IEnumerable<KeyValuePair<long, DateTime>> filesetslist = null, bool singleTimeMatch = false)
{
var filesets = (filesetslist ?? this.FilesetTimes).ToArray();
string query = "";
StringBuilder query = new StringBuilder();
var args = new List<object>();
if (time.Ticks > 0 || (versions != null && versions.Length > 0))
{
@@ -220,45 +220,44 @@ namespace Duplicati.Library.Main.Database
{
if (time.Kind == DateTimeKind.Unspecified)
throw new Exception("Invalid DateTime given, must be either local or UTC");
query += singleTimeMatch ? @" ""Timestamp"" = ?" : @" ""Timestamp"" <= ?";
query.Append(singleTimeMatch ? @" ""Timestamp"" = ?" : @" ""Timestamp"" <= ?");
// Make sure the resolution is the same (i.e. no milliseconds)
args.Add(NormalizeDateTimeToEpochSeconds(time));
hasTime = true;
}
if (versions != null && versions.Length > 0)
{
var qs = "";
StringBuilder qs = new StringBuilder();
foreach (var v in versions)
{
if (v >= 0 && v < filesets.Length)
{
args.Add(filesets[v].Key);
qs += "?,";
qs.Append("?,");
}
else
Logging.Log.WriteWarningMessage(LOGTAG, "SkipInvalidVersion", null, "Skipping invalid version: {0}", v);
}
if (qs.Length > 0)
{
qs = qs.Substring(0, qs.Length - 1);
if (hasTime)
query += " OR ";
query += @" ""ID"" IN (" + qs + ")";
query.Append(" OR ");
query.Append(@" ""ID"" IN (" + qs.ToString(0, qs.Length - 1) + ")");
}
}
if (!string.IsNullOrEmpty(query))
query = " WHERE " + query;
if (query.Length > 0)
{
query.Insert(0, " WHERE ");
}
}
return new Tuple<string, object[]>(query, args.ToArray());
return new Tuple<string, object[]>(query.ToString(), args.ToArray());
}
public long GetRemoteVolumeID(string file, System.Data.IDbTransaction transaction = null)
@@ -562,8 +561,8 @@ namespace Duplicati.Library.Main.Database
protected class TemporaryTransactionWrapper : IDisposable
{
private System.Data.IDbTransaction m_parent;
private bool m_isTemporary;
private readonly System.Data.IDbTransaction m_parent;
private readonly bool m_isTemporary;
public TemporaryTransactionWrapper(System.Data.IDbConnection connection, System.Data.IDbTransaction transaction)
{
@@ -605,7 +604,7 @@ namespace Duplicati.Library.Main.Database
private class LocalFileEntry : ILocalFileEntry
{
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public LocalFileEntry(System.Data.IDataReader reader)
{
m_reader = reader;
@@ -850,8 +849,8 @@ ON
{
private class BlocklistHashEnumerator : IEnumerator<string>
{
private System.Data.IDataReader m_reader;
private BlocklistHashEnumerable m_parent;
private readonly System.Data.IDataReader m_reader;
private readonly BlocklistHashEnumerable m_parent;
private string m_path = null;
private bool m_first = true;
private string m_current = null;
@@ -913,7 +912,7 @@ ON
}
}
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public BlocklistHashEnumerable(System.Data.IDataReader reader)
{
@@ -1143,8 +1142,8 @@ ORDER BY
public class FilteredFilenameTable : IDisposable
{
public string Tablename { get; private set; }
private System.Data.IDbConnection m_connection;
private readonly System.Data.IDbConnection m_connection;
public FilteredFilenameTable(System.Data.IDbConnection connection, Library.Utility.IFilter filter, System.Data.IDbTransaction transaction)
{
m_connection = connection;
@@ -69,12 +69,7 @@ namespace Duplicati.Library.Main.Database
{
cmd.Transaction = transaction;
var q = "";
foreach(var n in toDelete)
if (q.Length == 0)
q = "?";
else
q += ",?";
string q = String.Join(",", Enumerable.Repeat("?", toDelete.Length));
//First we remove unwanted entries
var deleted = cmd.ExecuteNonQuery(@"DELETE FROM ""Fileset"" WHERE ""Timestamp"" IN (" + q + @") ", toDelete.Select(x => NormalizeDateTimeToEpochSeconds(x)).Cast<object>().ToArray());
@@ -175,20 +170,20 @@ namespace Duplicati.Library.Main.Database
private class CompactReport : ICompactReport
{
private IEnumerable<VolumeUsage> m_report;
private IEnumerable<VolumeUsage> m_cleandelete;
private IEnumerable<VolumeUsage> m_wastevolumes;
private IEnumerable<VolumeUsage> m_smallvolumes;
private readonly IEnumerable<VolumeUsage> m_report;
private readonly IEnumerable<VolumeUsage> m_cleandelete;
private readonly IEnumerable<VolumeUsage> m_wastevolumes;
private readonly IEnumerable<VolumeUsage> m_smallvolumes;
private readonly long m_deletablevolumes;
private readonly long m_wastedspace;
private readonly long m_smallspace;
private readonly long m_fullsize;
private readonly long m_smallvolumecount;
private long m_deletablevolumes;
private long m_wastedspace;
private long m_smallspace;
private long m_fullsize;
private long m_smallvolumecount;
private long m_wastethreshold;
private long m_volsize;
private long m_maxsmallfilecount;
private readonly long m_wastethreshold;
private readonly long m_volsize;
private readonly long m_maxsmallfilecount;
public CompactReport(long volsize, long wastethreshold, long smallfilesize, long maxsmallfilecount, IEnumerable<VolumeUsage> report)
{
@@ -89,7 +89,7 @@ namespace Duplicati.Library.Main.Database
private class StorageHelper : IStorageHelper
{
private System.Data.IDbConnection m_connection;
private readonly System.Data.IDbConnection m_connection;
private System.Data.IDbTransaction m_transaction;
private System.Data.IDbCommand m_insertPreviousElementCommand;
@@ -56,9 +56,9 @@ namespace Duplicati.Library.Main.Database
private class FileSets : IFileSets
{
private System.Data.IDbConnection m_connection;
private readonly System.Data.IDbConnection m_connection;
private string m_tablename;
private KeyValuePair<long, DateTime>[] m_filesets;
private readonly KeyValuePair<long, DateTime>[] m_filesets;
public FileSets(LocalListDatabase owner, DateTime time, long[] versions)
{
@@ -94,7 +94,7 @@ namespace Duplicati.Library.Main.Database
private class Fileversion : IFileversion
{
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public string Path { get; private set; }
public bool More { get; private set; }
@@ -74,7 +74,7 @@ namespace Duplicati.Library.Main.Database
private readonly System.Data.IDbConnection m_connection;
private readonly System.Data.IDbTransaction m_transaction;
private readonly string m_tablename;
private LocalPurgeDatabase m_parentdb;
private readonly LocalPurgeDatabase m_parentdb;
public long ParentID { get; private set; }
public long RemovedFileCount { get; private set; }
@@ -49,23 +49,23 @@ namespace Duplicati.Library.Main.Database
}
}
private System.Data.IDbCommand m_insertFileCommand;
private System.Data.IDbCommand m_insertFilesetEntryCommand;
private System.Data.IDbCommand m_insertMetadatasetCommand;
private System.Data.IDbCommand m_insertBlocksetCommand;
private System.Data.IDbCommand m_insertBlocklistHashCommand;
private System.Data.IDbCommand m_updateBlockVolumeCommand;
private System.Data.IDbCommand m_insertBlockset;
private System.Data.IDbCommand m_insertSmallBlockset;
private System.Data.IDbCommand m_findBlocksetCommand;
private System.Data.IDbCommand m_findMetadatasetCommand;
private System.Data.IDbCommand m_findFilesetCommand;
private System.Data.IDbCommand m_findblocklisthashCommand;
private System.Data.IDbCommand m_findHashBlockCommand;
private System.Data.IDbCommand m_insertBlockCommand;
private System.Data.IDbCommand m_insertDuplicateBlockCommand;
private readonly System.Data.IDbCommand m_insertFileCommand;
private readonly System.Data.IDbCommand m_insertFilesetEntryCommand;
private readonly System.Data.IDbCommand m_insertMetadatasetCommand;
private readonly System.Data.IDbCommand m_insertBlocksetCommand;
private readonly System.Data.IDbCommand m_insertBlocklistHashCommand;
private readonly System.Data.IDbCommand m_updateBlockVolumeCommand;
private readonly System.Data.IDbCommand m_insertBlockset;
private readonly System.Data.IDbCommand m_insertSmallBlockset;
private readonly System.Data.IDbCommand m_findBlocksetCommand;
private readonly System.Data.IDbCommand m_findMetadatasetCommand;
private readonly System.Data.IDbCommand m_findFilesetCommand;
private readonly System.Data.IDbCommand m_findblocklisthashCommand;
private readonly System.Data.IDbCommand m_findHashBlockCommand;
private readonly System.Data.IDbCommand m_insertBlockCommand;
private readonly System.Data.IDbCommand m_insertDuplicateBlockCommand;
private PathLookupHelper<PathEntryKeeper> m_filesetLookup;
private readonly PathLookupHelper<PathEntryKeeper> m_filesetLookup;
private string m_tempblocklist;
private string m_tempsmalllist;
@@ -71,8 +71,8 @@ namespace Duplicati.Library.Main.Database
}
}
private System.Data.IDataReader m_rd;
private readonly System.Data.IDataReader m_rd;
public bool Done { get; private set; }
public BlockWithSources(System.Data.IDataReader rd)
@@ -135,11 +135,11 @@ namespace Duplicati.Library.Main.Database
private class MissingBlockList : IMissingBlockList
{
private System.Data.IDbConnection m_connection;
private TemporaryTransactionWrapper m_transaction;
private readonly System.Data.IDbConnection m_connection;
private readonly TemporaryTransactionWrapper m_transaction;
private System.Data.IDbCommand m_insertCommand;
private string m_tablename;
private string m_volumename;
private readonly string m_volumename;
public MissingBlockList(string volumename, System.Data.IDbConnection connection, System.Data.IDbTransaction transaction)
{
@@ -536,7 +536,7 @@ namespace Duplicati.Library.Main.Database
private class ExistingFile : IExistingFile
{
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public ExistingFile(System.Data.IDataReader rd) { m_reader = rd; HasMore = true; }
@@ -549,7 +549,7 @@ namespace Duplicati.Library.Main.Database
private class ExistingFileBlock : IExistingFileBlock
{
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public ExistingFileBlock(System.Data.IDataReader rd) { m_reader = rd; }
@@ -606,7 +606,7 @@ namespace Duplicati.Library.Main.Database
{
private class BlockSource : IBlockSource
{
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public BlockSource(System.Data.IDataReader rd) { m_reader = rd; }
public string Path { get { return m_reader.ConvertValueToString(6); } }
@@ -615,7 +615,7 @@ namespace Duplicati.Library.Main.Database
public bool IsMetadata { get { return false; } }
}
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public BlockDescriptor(System.Data.IDataReader rd) { m_reader = rd; HasMore = true; }
private string TargetPath { get { return m_reader.ConvertValueToString(0); } }
@@ -645,7 +645,7 @@ namespace Duplicati.Library.Main.Database
}
}
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public LocalBlockSource(System.Data.IDataReader rd) { m_reader = rd; HasMore = true; }
public string TargetPath { get { return m_reader.ConvertValueToString(0); } }
@@ -756,12 +756,12 @@ namespace Duplicati.Library.Main.Database
private class FilesAndMetadata : IFilesAndMetadata
{
private string m_tmptable;
private string m_filetablename;
private string m_blocktablename;
private long m_blocksize;
private readonly string m_tmptable;
private readonly string m_filetablename;
private readonly string m_blocktablename;
private readonly long m_blocksize;
private System.Data.IDbConnection m_connection;
private readonly System.Data.IDbConnection m_connection;
public FilesAndMetadata(System.Data.IDbConnection connection, string filetablename, string blocktablename, long blocksize, BlockVolumeReader curvolume)
{
@@ -805,7 +805,7 @@ namespace Duplicati.Library.Main.Database
{
private class PatchBlock : IPatchBlock
{
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public PatchBlock(System.Data.IDataReader rd) { m_reader = rd; }
public long Offset { get { return m_reader.ConvertValueToInt64(2); } }
@@ -813,7 +813,7 @@ namespace Duplicati.Library.Main.Database
public string Key { get { return m_reader.ConvertValueToString(4); } }
}
private System.Data.IDataReader m_reader;
private readonly System.Data.IDataReader m_reader;
public VolumePatch(System.Data.IDataReader rd) { m_reader = rd; HasMore = true; }
public string Path { get { return m_reader.ConvertValueToString(0); } }
@@ -1017,8 +1017,8 @@ namespace Duplicati.Library.Main.Database
private System.Data.IDbCommand m_statUpdateCommand;
private bool m_hasUpdates = false;
private string m_blocktablename;
private string m_filetablename;
private readonly string m_blocktablename;
private readonly string m_filetablename;
public System.Data.IDbTransaction Transaction { get { return m_insertblockCommand.Transaction; } }
@@ -1212,17 +1212,17 @@ namespace Duplicati.Library.Main.Database
{
private class BlockEntry : IBlockEntry
{
private System.Data.IDataReader m_rd;
private long m_blocksize;
private readonly System.Data.IDataReader m_rd;
private readonly long m_blocksize;
public BlockEntry(System.Data.IDataReader rd, long blocksize) { m_rd = rd; m_blocksize = blocksize; }
public long Offset { get { return m_rd.GetInt64(3) * m_blocksize; } }
public long Index { get { return m_rd.GetInt64(3); } }
public long Size { get { return m_rd.GetInt64(5); } }
public string Hash { get { return m_rd.GetString(4); } }
}
private System.Data.IDataReader m_rd;
private long m_blocksize;
private readonly System.Data.IDataReader m_rd;
private readonly long m_blocksize;
public FastSource(System.Data.IDataReader rd, long blocksize) { m_rd = rd; m_blocksize = blocksize; MoreData = true; }
public bool MoreData { get; private set; }
public string TargetPath { get { return m_rd.GetValue(0).ToString(); } }
@@ -63,7 +63,7 @@ namespace Duplicati.Library.Main.Database
var hotIx = Math.Min(m_lookup.Count, paths.Length) - 1;
while (hotIx >= 0)
{
if (path.StartsWith(m_lookup[hotIx].Key, Duplicati.Library.Utility.Utility.ClientFilenameStringComparision))
if (path.StartsWith(m_lookup[hotIx].Key, Duplicati.Library.Utility.Utility.ClientFilenameStringComparison))
{
c = m_lookup[hotIx].Value;
+7 -6
View File
@@ -202,12 +202,13 @@ namespace Duplicati.Library.Main
public static string GenerateRandomName()
{
var backupname = "";
var rnd = new Random();
for(var i = 0; i < 10; i++)
backupname += (char)rnd.Next('A', 'Z' + 1);
return backupname;
System.Text.StringBuilder backupName = new System.Text.StringBuilder();
for (var i = 0; i < 10; i++)
backupName.Append(rnd.Next('A', 'Z' + 1));
return backupName.ToString();
}
public static bool IsDatabasePathInUse(string path)
@@ -220,7 +221,7 @@ namespace Duplicati.Library.Main
if (!System.IO.File.Exists(file))
return false;
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<BackendEntry>>(System.IO.File.ReadAllText(file, System.Text.Encoding.UTF8)).Any(x => string.Equals(path, x.Databasepath, Library.Utility.Utility.ClientFilenameStringComparision));
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<BackendEntry>>(System.IO.File.ReadAllText(file, System.Text.Encoding.UTF8)).Any(x => string.Equals(path, x.Databasepath, Library.Utility.Utility.ClientFilenameStringComparison));
}
}
}
@@ -30,7 +30,7 @@ namespace Duplicati.Library.Main.Operation.Backup
internal class FlushRequest : IUploadRequest
{
public Task<long> LastWriteSizeAync { get { return m_tcs.Task; } }
private TaskCompletionSource<long> m_tcs = new TaskCompletionSource<long>();
private readonly TaskCompletionSource<long> m_tcs = new TaskCompletionSource<long>();
public void SetFlushed(long size)
{
m_tcs.TrySetResult(size);
@@ -33,7 +33,7 @@ namespace Duplicati.Library.Main.Operation.Backup
/// </summary>
internal class BackupDatabase : DatabaseCommon
{
private LocalBackupDatabase m_database;
private readonly LocalBackupDatabase m_database;
public class FileEntryData
{
@@ -26,7 +26,7 @@ namespace Duplicati.Library.Main.Operation.Backup
/// </summary>
internal class BackupStatsCollector : StatsCollector
{
private BackupResults m_res;
private readonly BackupResults m_res;
public BackupStatsCollector(BackupResults res)
: base(res.BackendWriter)
@@ -143,7 +143,7 @@ namespace Duplicati.Library.Main.Operation.Backup
var isDirectory = s[s.Length - 1] == System.IO.Path.DirectorySeparatorChar;
if (isDirectory)
{
while (pathstack.Count > 0 && !s.StartsWith(pathstack.Peek().Path, Library.Utility.Utility.ClientFilenameStringComparision))
while (pathstack.Count > 0 && !s.StartsWith(pathstack.Peek().Path, Library.Utility.Utility.ClientFilenameStringComparison))
{
var e = pathstack.Pop();
if (e.AnyEntries || pathstack.Count == 0)
@@ -158,7 +158,7 @@ namespace Duplicati.Library.Main.Operation.Backup
Logging.Log.WriteVerboseMessage(FILTER_LOGTAG, "ExcludingEmptyFolder", "Excluding empty folder {0}", e.Path);
}
if (pathstack.Count == 0 || s.StartsWith(pathstack.Peek().Path, Library.Utility.Utility.ClientFilenameStringComparision))
if (pathstack.Count == 0 || s.StartsWith(pathstack.Peek().Path, Library.Utility.Utility.ClientFilenameStringComparison))
{
pathstack.Push(new DirectoryStackEntry() { Path = s });
continue;
@@ -48,7 +48,7 @@ namespace Duplicati.Library.Main.Operation
private static readonly string LOGTAG = Logging.Log.LogTagFromType<BackupHandler>();
private readonly Options m_options;
private string m_backendurl;
private readonly string m_backendurl;
private LocalBackupDatabase m_database;
private System.Data.IDbTransaction m_transaction;
@@ -56,7 +56,7 @@ namespace Duplicati.Library.Main.Operation
private Library.Utility.IFilter m_filter;
private Library.Utility.IFilter m_sourceFilter;
private BackupResults m_result;
private readonly BackupResults m_result;
public BackupHandler(string backendurl, Options options, BackupResults results)
{
@@ -156,13 +156,13 @@ namespace Duplicati.Library.Main.Operation.Common
}
private DatabaseCommon m_database;
private readonly DatabaseCommon m_database;
private IBackend m_backend;
private Options m_options;
private string m_backendurl;
private readonly Options m_options;
private readonly string m_backendurl;
private bool m_uploadSuccess;
private StatsCollector m_stats;
private ITaskReader m_taskreader;
private readonly StatsCollector m_stats;
private readonly ITaskReader m_taskreader;
public BackendHandler(Options options, string backendUrl, DatabaseCommon database, StatsCollector stats, ITaskReader taskreader)
: base()
@@ -29,8 +29,8 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
private static readonly string LOGTAG = Logging.Log.LogTagFromType<CreateBugReportHandler>();
private string m_targetpath;
private Options m_options;
private CreateLogDatabaseResults m_result;
private readonly Options m_options;
private readonly CreateLogDatabaseResults m_result;
public CreateBugReportHandler(string targetpath, Options options, CreateLogDatabaseResults result)
{
@@ -34,7 +34,7 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
private static readonly string LOGTAG_RETENTION = LOGTAG + ":RetentionPolicy";
private DeleteResults m_result;
private readonly DeleteResults m_result;
protected string m_backendurl;
protected Options m_options;
@@ -24,8 +24,8 @@ namespace Duplicati.Library.Main.Operation
{
internal class ListAffected
{
private Options m_options;
private ListAffectedResults m_result;
private readonly Options m_options;
private readonly ListAffectedResults m_result;
public ListAffected(Options options, ListAffectedResults result)
{
@@ -29,9 +29,9 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
private static readonly string LOGTAG = Logging.Log.LogTagFromType(typeof(ListChangesHandler));
private string m_backendurl;
private Options m_options;
private ListChangesResults m_result;
private readonly string m_backendurl;
private readonly Options m_options;
private readonly ListChangesResults m_result;
public ListChangesHandler(string backend, Options options, ListChangesResults result)
{
@@ -23,9 +23,9 @@ namespace Duplicati.Library.Main.Operation
{
internal class ListControlFilesHandler
{
private Options m_options;
private string m_backendurl;
private ListResults m_result;
private readonly Options m_options;
private readonly string m_backendurl;
private readonly ListResults m_result;
public ListControlFilesHandler(string backendurl, Options options, ListResults result)
{
@@ -13,9 +13,9 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
private static readonly string LOGTAG = Logging.Log.LogTagFromType<ListFilesHandler>();
private string m_backendurl;
private Options m_options;
private ListResults m_result;
private readonly string m_backendurl;
private readonly Options m_options;
private readonly ListResults m_result;
public ListFilesHandler(string backend, Options options, ListResults result)
{
@@ -14,9 +14,9 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
private static readonly string LOGTAG = Logging.Log.LogTagFromType<RecreateDatabaseHandler>();
private string m_backendurl;
private Options m_options;
private RecreateDatabaseResults m_result;
private readonly string m_backendurl;
private readonly Options m_options;
private readonly RecreateDatabaseResults m_result;
public delegate IEnumerable<KeyValuePair<long, IParsedVolume>> NumberedFilterFilelistDelegate(IEnumerable<IParsedVolume> filelist);
public delegate void BlockVolumePostProcessor(string volumename,BlockVolumeReader reader);
@@ -14,9 +14,9 @@ namespace Duplicati.Library.Main.Operation
/// The tag used for logging
/// </summary>
private static readonly string LOGTAG = Logging.Log.LogTagFromType<RepairHandler>();
private string m_backendurl;
private Options m_options;
private RepairResults m_result;
private readonly string m_backendurl;
private readonly Options m_options;
private readonly RepairResults m_result;
public RepairHandler(string backend, Options options, RepairResults result)
{
@@ -7,9 +7,9 @@ namespace Duplicati.Library.Main.Operation
{
internal class RestoreControlFilesHandler
{
private Options m_options;
private string m_backendurl;
private RestoreControlFilesResults m_result;
private readonly Options m_options;
private readonly string m_backendurl;
private readonly RestoreControlFilesResults m_result;
public RestoreControlFilesHandler(string backendurl, Options options, RestoreControlFilesResults result)
{
@@ -15,10 +15,10 @@ namespace Duplicati.Library.Main.Operation
/// </summary>
private static readonly string LOGTAG = Logging.Log.LogTagFromType<RestoreHandler>();
private string m_backendurl;
private Options m_options;
private readonly string m_backendurl;
private readonly Options m_options;
private byte[] m_blockbuffer;
private RestoreResults m_result;
private readonly RestoreResults m_result;
private static readonly Snapshots.ISystemIO m_systemIO = Duplicati.Library.Utility.Utility.IsClientLinux ? (Snapshots.ISystemIO)new Snapshots.SystemIOLinux() : (Snapshots.ISystemIO)new Snapshots.SystemIOWindows();
private static readonly string DIRSEP = System.IO.Path.DirectorySeparatorChar.ToString();
@@ -31,8 +31,8 @@ namespace Duplicati.Library.Main.Operation
private static readonly string LOGTAG = Logging.Log.LogTagFromType<TestHandler>();
private readonly Options m_options;
private string m_backendurl;
private TestResults m_results;
private readonly string m_backendurl;
private readonly TestResults m_results;
public TestHandler(string backendurl, Options options, TestResults results)
{
@@ -8,8 +8,8 @@ namespace Duplicati.Library.Main.Operation
{
internal class VacuumHandler
{
private Options m_options;
private VacuumResult m_result;
private readonly Options m_options;
private readonly VacuumResult m_result;
public VacuumHandler(Options options, VacuumResult result)
{
+2 -3
View File
@@ -20,7 +20,6 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using Duplicati.Library.Interface;
using Duplicati.Library.Utility;
@@ -739,8 +738,8 @@ namespace Duplicati.Library.Main
{
if (!m_options.ContainsKey("tempdir") || string.IsNullOrEmpty(m_options["tempdir"]))
return System.IO.Path.GetTempPath();
else
return m_options["tempdir"];
return m_options["tempdir"];
}
}
+2 -2
View File
@@ -202,7 +202,7 @@ namespace Duplicati.Library.Main
protected Queue<DbMessage> m_dbqueue;
private TaskControlState m_controlState = TaskControlState.Run;
private System.Threading.ManualResetEvent m_pauseEvent = new System.Threading.ManualResetEvent(true);
private readonly System.Threading.ManualResetEvent m_pauseEvent = new System.Threading.ManualResetEvent(true);
public virtual ParsedResultType ParsedResult
{
@@ -863,7 +863,7 @@ namespace Duplicati.Library.Main
public override OperationMode MainOperation { get { return OperationMode.Test; } }
public IEnumerable<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>> Verifications { get { return m_verifications; } }
private List<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>> m_verifications = new List<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>>();
private readonly List<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>> m_verifications = new List<KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>>>();
public KeyValuePair<string, IEnumerable<KeyValuePair<TestEntryStatus, string>>> AddResult(string volume, IEnumerable<KeyValuePair<TestEntryStatus, string>> changes)
{
@@ -20,7 +20,7 @@ namespace Duplicati.Library.Main.Volumes
{
private class BlocklistHashEnumerator : IEnumerator<string>
{
private JsonReader m_reader;
private readonly JsonReader m_reader;
private string m_current;
private bool m_firstReset = true;
private bool m_done = false;
@@ -73,7 +73,7 @@ namespace Duplicati.Library.Main.Volumes
}
}
private JsonReader m_reader;
private readonly JsonReader m_reader;
public BlocklistHashEnumerable(JsonReader reader)
{
m_reader = reader;
@@ -103,7 +103,7 @@ namespace Duplicati.Library.Main.Volumes
public long Blocksize { get; private set; }
public IEnumerable<string> BlocklistHashes { get; private set; }
public IEnumerable<string> MetaBlocklistHashes { get; private set; }
private JsonReader m_reader;
private readonly JsonReader m_reader;
public FileEntry(JsonReader reader)
{
@@ -221,7 +221,7 @@ namespace Duplicati.Library.Main.Volumes
}
}
private ICompression m_compression;
private readonly ICompression m_compression;
private FileEntry m_current;
private StreamReader m_stream;
private JsonReader m_reader;
@@ -302,7 +302,7 @@ namespace Duplicati.Library.Main.Volumes
}
}
private ICompression m_compression;
private readonly ICompression m_compression;
public FileEntryEnumerable(ICompression compression)
{
m_compression = compression;
@@ -323,7 +323,7 @@ namespace Duplicati.Library.Main.Volumes
{
private class ControlFileEnumerator : IEnumerator<KeyValuePair<string, Stream>>
{
private ICompression m_compression;
private readonly ICompression m_compression;
private string[] m_files;
private long m_index;
private KeyValuePair<string, Stream>? m_current;
@@ -365,7 +365,7 @@ namespace Duplicati.Library.Main.Volumes
}
}
private ICompression m_compression;
private readonly ICompression m_compression;
public ControlFileEnumerable(ICompression compression)
{
m_compression = compression;
@@ -11,7 +11,7 @@ namespace Duplicati.Library.Main.Volumes
public class FilesetVolumeWriter : VolumeWriterBase
{
private StreamWriter m_streamwriter;
private JsonWriter m_writer;
private readonly JsonWriter m_writer;
private long m_filecount;
private long m_foldercount;
@@ -28,7 +28,7 @@ namespace Duplicati.Library.Main.Volumes
private class IndexBlockVolumeEnumerable : IEnumerable<IIndexBlockVolume>
{
private ICompression m_compression;
private readonly ICompression m_compression;
public IndexBlockVolumeEnumerable(ICompression compression)
{
@@ -41,8 +41,8 @@ namespace Duplicati.Library.Main.Volumes
{
private class BlockEnumerator : IEnumerator<KeyValuePair<string, long>>
{
private ICompression m_compression;
private string m_filename;
private readonly ICompression m_compression;
private readonly string m_filename;
private KeyValuePair<string, long>? m_current;
private System.IO.StreamReader m_stream;
private JsonReader m_reader;
@@ -139,8 +139,8 @@ namespace Duplicati.Library.Main.Volumes
}
}
private ICompression m_compression;
private string m_filename;
private readonly ICompression m_compression;
private readonly string m_filename;
private BlockEnumerator m_enumerator;
public BlockEnumerable(ICompression compression, string filename)
@@ -173,8 +173,8 @@ namespace Duplicati.Library.Main.Volumes
private class IndexBlockVolume : IIndexBlockVolume
{
private ICompression m_compression;
private string m_filename;
private readonly ICompression m_compression;
private readonly string m_filename;
private long? m_length;
private string m_hash;
private BlockEnumerable m_blocks;
@@ -231,7 +231,7 @@ namespace Duplicati.Library.Main.Volumes
}
}
private ICompression m_compression;
private readonly ICompression m_compression;
private IndexBlockVolume m_current;
private string[] m_files;
private long m_index;
@@ -284,8 +284,8 @@ namespace Duplicati.Library.Main.Volumes
private class IndexBlocklistEnumerable : IEnumerable<IIndexBlocklist>
{
private ICompression m_compression;
private long m_hashsize;
private readonly ICompression m_compression;
private readonly long m_hashsize;
public IndexBlocklistEnumerable(ICompression compression, long hashsize)
{
@@ -297,10 +297,10 @@ namespace Duplicati.Library.Main.Volumes
{
private class IndexBlocklist : IIndexBlocklist
{
private ICompression m_compression;
private string m_filename;
private long m_size;
private long m_hashsize;
private readonly ICompression m_compression;
private readonly string m_filename;
private readonly long m_size;
private readonly long m_hashsize;
public IndexBlocklist(ICompression compression, string filename, long size, long hashsize)
{
@@ -333,11 +333,11 @@ namespace Duplicati.Library.Main.Volumes
}
}
private ICompression m_compression;
private readonly ICompression m_compression;
private long m_index;
private KeyValuePair<string, long>[] m_files;
private IndexBlocklist m_current;
private long m_hashsize;
private readonly long m_hashsize;
public IndexBlocklistEnumerator(ICompression compression, long hashsize)
{
@@ -386,7 +386,7 @@ namespace Duplicati.Library.Main.Volumes
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
private long m_hashsize;
private readonly long m_hashsize;
public IndexVolumeReader(ICompression compression, Options options, long hashsize)
: base(compression, options)
@@ -59,11 +59,11 @@ namespace Duplicati.Library.Snapshots
/// <summary>
/// The path that is mapped to the drive
/// </summary>
private string m_targetPath;
private readonly string m_targetPath;
/// <summary>
/// A value indicating if the shell should be notified of changes
/// </summary>
private bool m_shellBroadcast;
private readonly bool m_shellBroadcast;
/// <summary>
/// Gets the drive that this mapping represents
+1 -1
View File
@@ -89,7 +89,7 @@ namespace Duplicati.Library.Snapshots
/// Enumerated Hyper-V guests
/// </summary>
public List<HyperVGuest> Guests { get { return m_Guests; } }
private List<HyperVGuest> m_Guests;
private readonly List<HyperVGuest> m_Guests;
public HyperVUtility()
{
+1 -1
View File
@@ -79,7 +79,7 @@ namespace Duplicati.Library.Snapshots
/// Enumerated MS SQL DBs
/// </summary>
public List<MSSQLDB> DBs { get { return m_DBs; } }
private List<MSSQLDB> m_DBs;
private readonly List<MSSQLDB> m_DBs;
public MSSQLUtility()
{
+2 -2
View File
@@ -153,7 +153,7 @@ namespace Duplicati.Library.Snapshots
{
if (r.UsnRecord.Usn >= minUsn
&& (reason == ChangeReason.Any || (MapChangeReason(r.UsnRecord.Reason) & reason) != 0)
&& (r.FullPath.Equals(sourceFileOrFolder, Utility.Utility.ClientFilenameStringComparision)
&& (r.FullPath.Equals(sourceFileOrFolder, Utility.Utility.ClientFilenameStringComparison)
|| isFolder && Utility.Utility.IsPathBelowFolder(r.FullPath, sourceFileOrFolder)))
{
yield return Tuple.Create(r.FullPath,
@@ -242,7 +242,7 @@ namespace Duplicati.Library.Snapshots
{
private readonly IReadOnlyCollection<byte> m_entryData;
private readonly IntPtr m_bufferPointer;
private GCHandle m_bufferHandle;
private readonly GCHandle m_bufferHandle;
private long m_offset;
public RecordEnumeratorImpl(IReadOnlyCollection<byte> entryData)
@@ -387,7 +387,7 @@ namespace Duplicati.Library.Snapshots
foreach (var folder in volumeData.Folders)
{
if (path.Equals(folder, Utility.Utility.ClientFilenameStringComparision))
if (path.Equals(folder, Utility.Utility.ClientFilenameStringComparison))
return true; // do not append from previous set, already scanned
if (Utility.Utility.IsPathBelowFolder(path, folder))
@@ -392,7 +392,7 @@ namespace Duplicati.Library.Snapshots
foreach (var kvp in m_volumeReverseMap)
{
if (snapshotPath.StartsWith(kvp.Key, Utility.Utility.ClientFilenameStringComparision))
if (snapshotPath.StartsWith(kvp.Key, Utility.Utility.ClientFilenameStringComparison))
return Path.Combine(kvp.Value, snapshotPath.Substring(kvp.Key.Length));
}
@@ -17,7 +17,7 @@ namespace Duplicati.Library.Utility
/// <summary>
/// The <see cref="System.Net.HttpWebRequest"/> method being wrapped
/// </summary>
private WebRequest m_request;
private readonly WebRequest m_request;
/// <summary>
/// The current internal state of the object
/// </summary>
@@ -37,7 +37,7 @@ namespace Duplicati.Library.Utility
/// <summary>
/// The activity timeout value
/// </summary>
private int m_activity_timeout = (int)TimeSpan.FromSeconds(30).TotalMilliseconds;
private readonly int m_activity_timeout = (int)TimeSpan.FromSeconds(30).TotalMilliseconds;
/// <summary>
/// List of valid states
@@ -186,13 +186,13 @@ namespace Duplicati.Library.Utility
/// </summary>
private class AsyncWrapper
{
private IAsyncResult m_async = null;
private readonly IAsyncResult m_async = null;
private Stream m_stream = null;
private WebResponse m_response = null;
private AsyncHttpRequest m_owner;
private readonly AsyncHttpRequest m_owner;
private Exception m_exception = null;
private ManualResetEvent m_event = new ManualResetEvent(false);
private bool m_isRequest;
private readonly ManualResetEvent m_event = new ManualResetEvent(false);
private readonly bool m_isRequest;
private bool m_timedout = false;
public AsyncWrapper(AsyncHttpRequest owner, bool isRequest)

Some files were not shown because too many files have changed in this diff Show More