diff options
10 files changed, 165 insertions, 73 deletions
diff --git a/Duplicati/Library/Backend/Dropbox/Dropbox.cs b/Duplicati/Library/Backend/Dropbox/Dropbox.cs index 52a3d3feb..27b88d9c1 100644 --- a/Duplicati/Library/Backend/Dropbox/Dropbox.cs +++ b/Duplicati/Library/Backend/Dropbox/Dropbox.cs @@ -138,7 +138,7 @@ namespace Duplicati.Library.Backend public string[] DNSName
{
- get { return new string[] { new Uri(DropboxHelper.API_URL).Host, new Uri(DropboxHelper.CONTENT_API_URL).Host }; }
+ get { return WebApi.Dropbox.Hosts(); }
}
public void Test()
diff --git a/Duplicati/Library/Backend/Dropbox/DropboxHelper.cs b/Duplicati/Library/Backend/Dropbox/DropboxHelper.cs index 15d998d9e..f8a37df46 100644 --- a/Duplicati/Library/Backend/Dropbox/DropboxHelper.cs +++ b/Duplicati/Library/Backend/Dropbox/DropboxHelper.cs @@ -1,8 +1,7 @@ using System;
-using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Net;
+
using Duplicati.Library.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -11,8 +10,6 @@ namespace Duplicati.Library.Backend {
public class DropboxHelper : OAuthHelper
{
- internal const string API_URL = "https://api.dropboxapi.com/2";
- internal const string CONTENT_API_URL = "https://content.dropboxapi.com/2";
private const int DROPBOX_MAX_CHUNK_UPLOAD = 10 * 1024 * 1024; // 10 MB max upload
private const string API_ARG_HEADER = "DROPBOX-API-arg";
@@ -25,18 +22,18 @@ namespace Duplicati.Library.Backend public ListFolderResult ListFiles(string path)
{
- var pa = new PathArg();
- pa.path = path;
-
- var url = string.Format("{0}/files/list_folder", API_URL);
+ var pa = new PathArg + { + path = path + };
try
{
- return PostAndGetJSONData<ListFolderResult>(url, pa);
+ return PostAndGetJSONData<ListFolderResult>(WebApi.Dropbox.ListFilesUrl(), pa);
}
catch (Exception ex)
{
- handleDropboxException(ex, false);
+ HandleDropboxException(ex, false);
throw;
}
}
@@ -44,15 +41,14 @@ namespace Duplicati.Library.Backend public ListFolderResult ListFilesContinue(string cursor)
{
var lfca = new ListFolderContinueArg() { cursor = cursor };
- var url = string.Format("{0}/files/list_folder/continue", API_URL);
try
{
- return PostAndGetJSONData<ListFolderResult>(url, lfca);
+ return PostAndGetJSONData<ListFolderResult>(WebApi.Dropbox.ListFilesContinueUrl(), lfca);
}
catch (Exception ex)
{
- handleDropboxException(ex, false);
+ HandleDropboxException(ex, false);
throw;
}
}
@@ -60,15 +56,14 @@ namespace Duplicati.Library.Backend public FolderMetadata CreateFolder(string path)
{
var pa = new PathArg() { path = path };
- var url = string.Format("{0}/files/create_folder", API_URL);
try
{
- return PostAndGetJSONData<FolderMetadata>(url, pa);
+ return PostAndGetJSONData<FolderMetadata>(WebApi.Dropbox.CreateFolderUrl(), pa);
}
catch (Exception ex)
{
- handleDropboxException(ex, false);
+ HandleDropboxException(ex, false);
throw;
}
}
@@ -80,8 +75,7 @@ namespace Duplicati.Library.Backend var chunksize = (int)Math.Min(DROPBOX_MAX_CHUNK_UPLOAD, stream.Length);
- var url = string.Format("{0}/files/upload_session/start", CONTENT_API_URL);
- var req = CreateRequest(url, "POST");
+ var req = CreateRequest(WebApi.Dropbox.UploadSessionStartUrl(), "POST");
req.Headers[API_ARG_HEADER] = JsonConvert.SerializeObject(ussa);
req.ContentType = "application/octet-stream";
req.ContentLength = chunksize;
@@ -118,11 +112,10 @@ namespace Duplicati.Library.Backend usaa.cursor.session_id = ussr.session_id;
usaa.cursor.offset = globalBytesRead;
usaa.close = remaining < DROPBOX_MAX_CHUNK_UPLOAD;
- url = string.Format("{0}/files/upload_session/append_v2", CONTENT_API_URL);
chunksize = (int)Math.Min(DROPBOX_MAX_CHUNK_UPLOAD, (long)remaining);
- req = CreateRequest(url, "POST");
+ req = CreateRequest(WebApi.Dropbox.UploadSessionAppendUrl(), "POST");
req.Headers[API_ARG_HEADER] = JsonConvert.SerializeObject(usaa);
req.ContentType = "application/octet-stream";
req.ContentLength = chunksize;
@@ -158,8 +151,7 @@ namespace Duplicati.Library.Backend usfa.cursor.offset = (ulong)globalBytesRead;
usfa.commit.path = path;
- url = string.Format("{0}/files/upload_session/finish", CONTENT_API_URL);
- req = CreateRequest(url, "POST");
+ req = CreateRequest(WebApi.Dropbox.UploadSessionFinishUrl(), "POST");
req.Headers[API_ARG_HEADER] = JsonConvert.SerializeObject(usfa);
req.ContentType = "application/octet-stream";
req.Timeout = 200000;
@@ -168,7 +160,7 @@ namespace Duplicati.Library.Backend }
catch (Exception ex)
{
- handleDropboxException(ex, true);
+ HandleDropboxException(ex, true);
throw;
}
}
@@ -177,9 +169,9 @@ namespace Duplicati.Library.Backend {
try
{
- var pa = new PathArg() { path = path };
- var url = string.Format("{0}/files/download", CONTENT_API_URL);
- var req = CreateRequest(url, "POST");
+ var pa = new PathArg { path = path };
+
+ var req = CreateRequest(WebApi.Dropbox.DownloadFilesUrl(), "POST");
req.Headers[API_ARG_HEADER] = JsonConvert.SerializeObject(pa);
using (var response = GetResponse(req))
@@ -187,7 +179,7 @@ namespace Duplicati.Library.Backend }
catch (Exception ex)
{
- handleDropboxException(ex, true);
+ HandleDropboxException(ex, true);
throw;
}
}
@@ -197,19 +189,18 @@ namespace Duplicati.Library.Backend try
{
var pa = new PathArg() { path = path };
- var url = string.Format("{0}/files/delete", API_URL);
- using (var response = GetResponse(url, pa))
+ using (var response = GetResponse(WebApi.Dropbox.DeleteUrl(), pa))
using(var sr = new StreamReader(response.GetResponseStream()))
sr.ReadToEnd();
}
catch (Exception ex)
{
- handleDropboxException(ex, true);
+ HandleDropboxException(ex, true);
throw;
}
}
- private void handleDropboxException(Exception ex, bool filerequest)
+ private void HandleDropboxException(Exception ex, bool filerequest)
{
if (ex is WebException)
{
diff --git a/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj b/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj index 75e2c7264..780fc7a85 100644 --- a/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj +++ b/Duplicati/Library/Backend/Dropbox/Duplicati.Library.Backend.Dropbox.csproj @@ -42,6 +42,7 @@ <Compile Include="DropboxHelper.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Strings.cs" /> + <Compile Include="WebApi.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\Interface\Duplicati.Library.Interface.csproj"> diff --git a/Duplicati/Library/Backend/Dropbox/WebApi.cs b/Duplicati/Library/Backend/Dropbox/WebApi.cs new file mode 100644 index 000000000..1d0f2af86 --- /dev/null +++ b/Duplicati/Library/Backend/Dropbox/WebApi.cs @@ -0,0 +1,91 @@ +// 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 Duplicati.Library.Utility;
+
+namespace Duplicati.Library.Backend.WebApi
+{
+ public static class Dropbox
+ {
+ public static string CreateFolderUrl()
+ {
+ return Uri.UriBuilder(Url.API, Path.CreateFolder);
+ }
+
+ public static string ListFilesUrl()
+ {
+ return Uri.UriBuilder(Url.API, Path.ListFolder);
+ }
+
+ public static string ListFilesContinueUrl()
+ {
+ return Uri.UriBuilder(Url.API, Path.ListFolderContinue);
+ }
+
+ public static string DeleteUrl()
+ {
+ return Uri.UriBuilder(Url.API, Path.DeleteFolder);
+ }
+
+ public static string UploadSessionStartUrl() + {
+ return Uri.UriBuilder(Url.CONTENT_API_URL, Path.UploadSessionStart);
+ }
+
+ public static string UploadSessionAppendUrl()
+ {
+ return Uri.UriBuilder(Url.CONTENT_API_URL, Path.UploadSessionAppend);
+ }
+
+ public static string UploadSessionFinishUrl()
+ {
+ return Uri.UriBuilder(Url.CONTENT_API_URL, Path.UploadSessionFinish);
+ }
+
+ public static string DownloadFilesUrl()
+ {
+ return Uri.UriBuilder(Url.CONTENT_API_URL, Path.DownloadFiles);
+ }
+
+ public static string[] Hosts()
+ {
+ return new[] { new System.Uri(Url.API).Host, new System.Uri(Url.CONTENT_API_URL).Host };
+
+ }
+
+ private static class Url
+ {
+ public const string API = "https://api.dropboxapi.com/2";
+ public const string CONTENT_API_URL = "https://content.dropboxapi.com/2";
+ }
+
+ private static class Path
+ {
+ public const string CreateFolder = "files/create_folder";
+ public const string DeleteFolder = "files/delete";
+ public const string ListFolder = "files/list_folder";
+ public const string ListFolderContinue = "files/list_folder/continue";
+
+ public const string UploadSessionStart = "files/upload_session/start";
+ public const string UploadSessionAppend = "files/upload_session/append_v2";
+ public const string UploadSessionFinish = "files/upload_session/finish";
+
+ public const string DownloadFiles = "files/download";
+ }
+
+ }
+}
\ No newline at end of file diff --git a/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs b/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs index 8e7083881..3205083b3 100644 --- a/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs +++ b/Duplicati/Library/Backend/OneDrive/MicrosoftGraphBackend.cs @@ -124,7 +124,7 @@ namespace Duplicati.Library.Backend // Extract out the path to the backup root folder from the given URI. Since this can be an expensive operation,
// we will cache the value using a lazy initializer.
- this.rootPathFromURL = new Lazy<string>(() => this.GetRootPathFromUrl(url));
+ this.rootPathFromURL = new Lazy<string>(() => MicrosoftGraphBackend.NormalizeSlashes(this.GetRootPathFromUrl(url)));
}
public abstract string ProtocolKey { get; }
diff --git a/Duplicati/Library/Snapshots/HyperVUtility.cs b/Duplicati/Library/Snapshots/HyperVUtility.cs index 306b9aa80..cdaa8ce84 100644 --- a/Duplicati/Library/Snapshots/HyperVUtility.cs +++ b/Duplicati/Library/Snapshots/HyperVUtility.cs @@ -101,7 +101,7 @@ namespace Duplicati.Library.Snapshots }
//Set the namespace depending off host OS
- _wmiv2Namespace = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2;
+ _wmiv2Namespace = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2);
//Set the scope to use in WMI. V2 for Server 2012 or newer.
_wmiScope = _wmiv2Namespace
diff --git a/Duplicati/Library/Utility/FilterGroups.cs b/Duplicati/Library/Utility/FilterGroups.cs index ac93c8fb5..db9757403 100644 --- a/Duplicati/Library/Utility/FilterGroups.cs +++ b/Duplicati/Library/Utility/FilterGroups.cs @@ -295,7 +295,7 @@ namespace Duplicati.Library.Utility yield return FilterGroups.CreateWildcardFilter(@"*/Google/Chrome/Safe Browsing*");
yield return FilterGroups.CreateWildcardFilter(@"*/iPhoto Library/iPod Photo Cache/");
yield return FilterGroups.CreateWildcardFilter(@"*/Mozilla/Firefox/*cache*");
- yield return FilterGroups.CreateRegexFilter(@".*/(cookies|permissions).sqllite(-.{3})?");
+ yield return FilterGroups.CreateRegexFilter(@".*/(cookies|permissions).sqlite(-.{3})?");
}
if (group.HasFlag(FilterGroup.TemporaryFiles))
diff --git a/Duplicati/Server/webroot/ngax/scripts/controllers/HomeController.js b/Duplicati/Server/webroot/ngax/scripts/controllers/HomeController.js index 3b6266fb0..f66252270 100644 --- a/Duplicati/Server/webroot/ngax/scripts/controllers/HomeController.js +++ b/Duplicati/Server/webroot/ngax/scripts/controllers/HomeController.js @@ -1,4 +1,4 @@ -backupApp.controller('HomeController', function ($scope, $location, ServerStatus, BackupList, AppService, DialogService, gettextCatalog) { +backupApp.controller('HomeController', function ($scope, $location, ServerStatus, BackupList, AppService, AppUtils, DialogService, gettextCatalog) { $scope.backups = BackupList.watch($scope); $scope.doRun = function(id) { @@ -57,22 +57,5 @@ backupApp.controller('HomeController', function ($scope, $location, ServerStatus AppService.post('/backup/' + id + '/createreport'); }; - $scope.formatDuration = function(duration) { - // parse days if timespan is over 24 hours long - var days = 0; - if (duration != null && duration.indexOf(".") < 7) { - days = duration.substring(0, duration.indexOf(".")); - duration = duration.substring(duration.indexOf(".")+1, duration.length); - } - - // strip miliseconds - if (duration != null && duration.indexOf(".") > 0) - duration = duration.substring(0, duration.indexOf(".")); - - // prefix the days if applicable - if (days != 0) - return days + ":" + duration; - else - return duration; - }; + $scope.formatDuration = AppUtils.formatDuration; }); diff --git a/Duplicati/Server/webroot/ngax/scripts/controllers/RestoreWizardController.js b/Duplicati/Server/webroot/ngax/scripts/controllers/RestoreWizardController.js index 702ab8985..66c9069f1 100644 --- a/Duplicati/Server/webroot/ngax/scripts/controllers/RestoreWizardController.js +++ b/Duplicati/Server/webroot/ngax/scripts/controllers/RestoreWizardController.js @@ -1,4 +1,4 @@ -backupApp.controller('RestoreWizardController', function($scope, $location, BackupList, gettextCatalog) { +backupApp.controller('RestoreWizardController', function($scope, $location, BackupList, AppUtils, gettextCatalog) { $scope.backups = BackupList.watch($scope); $scope.selection = { @@ -13,4 +13,6 @@ backupApp.controller('RestoreWizardController', function($scope, $location, Back else $location.path('/restore/' + $scope.selection.backupid); }; + + $scope.formatDuration = AppUtils.formatDuration; }); diff --git a/Duplicati/Server/webroot/ngax/scripts/services/AppUtils.js b/Duplicati/Server/webroot/ngax/scripts/services/AppUtils.js index 39e9519fd..1495f2b5c 100644 --- a/Duplicati/Server/webroot/ngax/scripts/services/AppUtils.js +++ b/Duplicati/Server/webroot/ngax/scripts/services/AppUtils.js @@ -101,12 +101,12 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer ]; apputils.daysOfWeek = [ - {name: gettextCatalog.getString('Mon'), value: 'mon'}, - {name: gettextCatalog.getString('Tue'), value: 'tue'}, - {name: gettextCatalog.getString('Wed'), value: 'wed'}, - {name: gettextCatalog.getString('Thu'), value: 'thu'}, - {name: gettextCatalog.getString('Fri'), value: 'fri'}, - {name: gettextCatalog.getString('Sat'), value: 'sat'}, + {name: gettextCatalog.getString('Mon'), value: 'mon'}, + {name: gettextCatalog.getString('Tue'), value: 'tue'}, + {name: gettextCatalog.getString('Wed'), value: 'wed'}, + {name: gettextCatalog.getString('Thu'), value: 'thu'}, + {name: gettextCatalog.getString('Fri'), value: 'fri'}, + {name: gettextCatalog.getString('Sat'), value: 'sat'}, {name: gettextCatalog.getString('Sun'), value: 'sun'} ]; @@ -180,7 +180,7 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer key: '-', prefix: '-' }]; - + apputils.filterGroups = [{ name: gettextCatalog.getString('Default excludes'), value: 'DefaultExcludes' @@ -207,17 +207,17 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer apputils.filterTypeMap = {}; for (var i = apputils.filterClasses.length - 1; i >= 0; i--) - apputils.filterTypeMap[apputils.filterClasses[i].key] = apputils.filterClasses[i]; + apputils.filterTypeMap[apputils.filterClasses[i].key] = apputils.filterClasses[i]; - $rootScope.$broadcast('apputillookupschanged'); + $rootScope.$broadcast('apputillookupschanged'); }; reloadTexts(); - $rootScope.$on('gettextLanguageChanged', reloadTexts); + $rootScope.$on('gettextLanguageChanged', reloadTexts); this.parseBoolString = function(txt, def) { txt = (txt || '').toLowerCase(); - if (txt == '0' || txt == 'false' || txt == 'off' || txt == 'no' || txt == 'f') + if (txt == '0' || txt == 'false' || txt == 'off' || txt == 'no' || txt == 'f') return false; else if (txt == '1' || txt == 'true' || txt == 'on' || txt == 'yes' || txt == 't') return true; @@ -255,7 +255,7 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer } else return new Date(dt); - }; + }; this.parseOptionStrings = function(val, dict, validateCallback) { dict = dict || {}; @@ -390,7 +390,7 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer pwd[pos] = t; } - return pwd.join(''); + return pwd.join(''); } this.nl2br = function(str, is_xhtml) { @@ -533,7 +533,7 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer x.splice(i, 1); }; - return x; + return x; }; this.splitFilterIntoTypeAndBody = function(src, dirsep) { @@ -599,18 +599,18 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer for(var i = 0; i < filters.length; i++) { var f = filters[i]; - + if (f == null || f.length == 0) continue; var flag = f.substr(0, 1); var filter = f.substr(1); var rx = filter.substr(0, 1) == '[' && filter.substr(filter.length - 1, 1) == ']'; - if (rx) + if (rx) filter = filter.substr(1, filter.length - 2); else filter = this.replace_all(this.replace_all(this.preg_quote(filter), '*', '.*'), '?', '.'); - + try { res.push([flag == '+', new RegExp(filter, caseSensitive ? 'g' : 'gi')]); } catch (e) { @@ -641,7 +641,7 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer function copyToList(lst, key) { if (key != null && typeof(key) != typeof('')) key = null; - + for(var n in lst) { if (key == null || key.toLowerCase() == lst[n].Key.toLowerCase()) @@ -686,4 +686,28 @@ backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, DialogSer } }; + this.formatDuration = function(duration) { + // duration is a timespan string in format (dd.)hh:mm:ss.sss + // the part (dd.) is as indicated optional + + if (duration == null) { + return; + } + + var timespanArray = duration.split(':'); + + if (timespanArray.length < 3) { + return; + } + + if (timespanArray[0].indexOf('.') > 0) { + timespanArray[0] = timespanArray[0].replace('.', " day(s) and "); + } + + // remove ms + timespanArray[2] = timespanArray[2].substring(0, timespanArray[2].indexOf(".")); + + return timespanArray.join(':'); + }; + }); |