Skip to content
This repository has been archived by the owner on Jan 24, 2024. It is now read-only.

Migrate DropBox API to v2 #35

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,150 changes: 1,076 additions & 1,074 deletions KeePass/KeePass.csproj

Large diffs are not rendered by default.

57 changes: 32 additions & 25 deletions KeePass/Sources/DropBox/DropBoxAdapter.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
using System;
using System.IO;
using DropNet;
using DropNet.Models;
using Dropbox.Api;
using Dropbox.Api.Files;
using KeePass.IO.Utils;
using KeePass.Storage;

namespace KeePass.Sources.DropBox
{
internal class DropBoxAdapter : ServiceAdapterBase
{
private DropNetClient _client;
private DropboxClientWrapper _client;
private SyncInfo _info;

public override void Conflict(ListItem item,
Expand All @@ -25,8 +25,9 @@ public override void Conflict(ListItem item,
public override void Download(ListItem item,
Action<ListItem, byte[]> downloaded)
{
_client.GetFileAsync(_info.Path,
x => downloaded(item, x.RawBytes),
DropBoxUtils.CallAsync(
() => _client.Client.Files.DownloadAsync(DropBoxUtils.RenderUrl(_info.Path)),
async t => downloaded(item, await t.GetContentAsByteArrayAsync()),
OnError);
}

Expand All @@ -37,7 +38,15 @@ public override SyncInfo Initialize(DatabaseInfo info)

var details = info.Details;
var url = new Uri(details.Url);
_client = CreateClient(url.UserInfo);
var userInfo = url.UserInfo;
if (DropBoxUtils.UpdateAuth(ref userInfo))
{
var urlBld = new UriBuilder(url);
urlBld.UserName = userInfo;
urlBld.Password = null;
details.Url = urlBld.Uri.ToString();
}
_client = CreateClient(userInfo);

_info = new SyncInfo
{
Expand All @@ -60,7 +69,8 @@ public override SyncInfo Initialize(DatabaseInfo info)

public override void List(Action<ListItem> ready)
{
_client.GetMetaDataAsync(_info.Path,
DropBoxUtils.CallAsync(
() => _client.Client.Files.GetMetadataAsync(DropBoxUtils.RenderUrl(_info.Path)),
meta => ready(Translate(meta)),
OnError);
}
Expand All @@ -72,12 +82,9 @@ public override void Upload(ListItem item,
meta => uploaded(Translate(meta)));
}

private static DropNetClient CreateClient(string userInfo)
private static DropboxClientWrapper CreateClient(string userInfo)
{
var parts = userInfo.Split(':');

return DropBoxUtils.Create(
parts[0], parts[1]);
return DropBoxUtils.Create(userInfo);
}

private string GetNonConflictPath()
Expand All @@ -96,29 +103,29 @@ private string GetNonConflictPath()
.Replace('\\', '/');
}

private static ListItem Translate(MetaData meta)
private static ListItem Translate(Metadata meta)
{
return new ListItem
{
Tag = meta,
Timestamp = meta.Modified,
Timestamp = meta.IsFile ? meta.AsFile.ClientModified.ToString("r") : string.Empty,
};
}

private void UploadFileAsync(string path,
Action<MetaData> completed)
Action<Metadata> completed)
{
var orgPath = path;

var fileName = Path.GetFileName(path);
path = Path.GetDirectoryName(path)
.Replace('\\', '/');

_client.UploadFileAsync(path,
fileName, _info.Database,
x => _client.GetMetaDataAsync(
orgPath, completed, OnError),
OnError);
var stream = new MemoryStream(_info.Database);
DropBoxUtils.CallAsyncAndDispose(
() => _client.Client.Files.UploadAsync(DropBoxUtils.RenderUrl(path.Replace('\\', '/')), body: stream),
x => DropBoxUtils.CallAsync(
() => _client.Client.Files.GetMetadataAsync(DropBoxUtils.RenderUrl(orgPath)),
completed,
OnError),
OnError,
stream);
}
}
}
57 changes: 32 additions & 25 deletions KeePass/Sources/DropBox/DropBoxAuth.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Net;
using System.Windows;
using DropNet;
using Dropbox.Api;
using KeePass.Utils;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
Expand All @@ -11,29 +11,41 @@ namespace KeePass.Sources.DropBox
public partial class DropBoxAuth
{
private const string CALL_BACK = "https://github.com/gkardava/WinPass/blob/master/README.md"; //TODO remove url
// CALL_BACK is the url registered in the App Console. According to DropBox it is common to use a localhost URI.

private readonly DropNetClient _client;
private readonly ProgressIndicator _indicator;
private readonly string _oauth2State = Guid.NewGuid().ToString("N");

public DropBoxAuth()
{
InitializeComponent();

_indicator = AddIndicator();
_client = DropBoxUtils.Create();
}

private void CheckToken()
private void CheckToken(Uri uri)
{
_client.GetAccessTokenAsync(x =>
{
var folder = NavigationContext
.QueryString["folder"];

this.NavigateTo<List>(
"token={0}&secret={1}&folder={2}",
x.Token, x.Secret, folder);
}, ex => ShowError());
try
{
var result = DropboxOAuth2Helper.ParseTokenFragment(uri);
if (result.State != _oauth2State)
{
ShowError();
}
else
{
var folder = NavigationContext
.QueryString["folder"];

this.NavigateTo<List>(
"token={0}&folder={1}",
result.AccessToken, folder);
}
}
catch(ArgumentException)
{
ShowError();
}
}

private void ShowError()
Expand All @@ -56,24 +68,19 @@ private void browser_Loaded(object sender, RoutedEventArgs e)
if (!Network.CheckNetwork())
return;

_client.GetTokenAsync(x =>
{
var url = _client.BuildAuthorizeUrl(x, CALL_BACK);

url = "https://www.dropbox.com/logout?cont=" +
HttpUtility.UrlEncode(url);

Dispatcher.BeginInvoke(() =>
browser.Navigate(new Uri(url)));
}, ex => ShowError());
var authorizeUri = DropboxOAuth2Helper.GetAuthorizeUri(OAuthResponseType.Token, ApiKeys.DROPBOX_KEY, CALL_BACK, state: _oauth2State);
Dispatcher.BeginInvoke(() => browser.Navigate(authorizeUri));
}

private void browser_Navigating(object sender, NavigatingEventArgs e)
{
_indicator.IsVisible = !e.Cancel;

if (e.Uri.ToString().StartsWith(CALL_BACK))
CheckToken();
if (e.Uri.ToString().StartsWith(CALL_BACK))
{
e.Cancel = true;
CheckToken(e.Uri);
}
}
}
}
94 changes: 77 additions & 17 deletions KeePass/Sources/DropBox/DropBoxUtils.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,96 @@
using System;
using DropNet;
using Dropbox.Api;
using KeePass.Utils;

namespace KeePass.Sources.DropBox
{
internal class DropboxClientWrapper
{
internal DropboxClientWrapper(string token)
{
Token = token;
Client = new DropboxClient(token);
}

internal DropboxClient Client { get; }
internal string Token { get; }
}

internal static class DropBoxUtils
{
public static DropNetClient Create()
{
return new DropNetClient(
ApiKeys.DROPBOX_KEY,
ApiKeys.DROPBOX_SECRET);
public static bool UpdateAuth(ref string token)
{
if (token.Contains(":"))
{
var auth = new DropboxAppClient(
ApiKeys.DROPBOX_KEY,
ApiKeys.DROPBOX_SECRET);
var oldToken = token.Split(new[] { ':' }, 2);
var auth2 = auth.Auth.TokenFromOauth1Async(oldToken[0], oldToken[1]);
auth2.RunSynchronously();
token = auth2.Result.Oauth2Token;
return true;
}
return false;
}

public static DropNetClient Create(
string token, string secret)
public static DropboxClientWrapper Create(string token)
{
return new DropNetClient(
ApiKeys.DROPBOX_KEY,
ApiKeys.DROPBOX_SECRET,
token, secret);
return new DropboxClientWrapper(token);
}

public static string GetUrl(
this DropNetClient client,
this DropboxClientWrapper client,
string path)
{
var login = client.UserLogin;

return string.Format(
"dropbox://{0}:{1}@dropbox.com{2}",
login.Token, login.Secret, path);
"dropbox://{0}@{1}",
client.Token, path);
}

public static string RenderUrl(string uri)
{
var parts = uri.Split(new[] { '@' }, 2);
return parts.Length == 2 ? parts[1] : parts[0];
}

public static async void CallAsyncAndDispose<T>(Func<System.Threading.Tasks.Task<T>> method, Action<T> onSuccess, Action<DropboxException> onError, IDisposable dispose)
{
using (dispose)
{
await CallAsyncImpl(method, onSuccess, onError);
}
}

public static async void CallAsync<T>(Func<System.Threading.Tasks.Task<T>> method, Action<T> onSuccess, Action<DropboxException> onError)
{
await CallAsyncImpl(method, onSuccess, onError);
}

private static async System.Threading.Tasks.Task CallAsyncImpl<T>(Func<System.Threading.Tasks.Task<T>> method, Action<T> onSuccess, Action<DropboxException> onError)
{
var task = method();
try
{
var result = await task;
onSuccess(result);
}
catch
{
if (task.Exception != null)
{
foreach (var ex in task.Exception.InnerExceptions)
{
if (ex is DropboxException)
{
onError(ex as DropboxException);
return;
}
}
throw new ApplicationException("Unexpected exception. See InnerException for details.", task.Exception);
}
throw;
}
}
}
}
Loading