diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..c77696ad37 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/bin/Debug/net5.0/Dropbox.Api.Integration.Tests.dll", + "args": [], + "cwd": "${workspaceFolder}/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..88c4a54f64 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/Dropbox.Api.Integration.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/Dropbox.Api.Integration.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/Dropbox.Api.Integration.Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Setup.ps1 b/Setup.ps1 new file mode 100644 index 0000000000..515dda7119 --- /dev/null +++ b/Setup.ps1 @@ -0,0 +1,36 @@ +[CmdletBinding()] +param ( + [Parameter(Mandatory)][string]$DbxAppKey, + [Parameter(Mandatory)][string]$DbxAppSecret, + [Parameter(Mandatory)][string]$DbxUserAccessToken, + [System.EnvironmentVariableTarget]$Scope = [System.EnvironmentVariableTarget]::User +) + +Function Script:Set-EnvironmentVariable { + [CmdletBinding()] + param ( + [Parameter(Mandatory)][string]$Variable, + [Parameter(Mandatory)][string]$Value, + [Parameter(Mandatory)][System.EnvironmentVariableTarget]$Scope + ) + [System.Environment]::SetEnvironmentVariable($Variable, $Value, $Scope) + Set-Content -Path Env:\$Variable -Value $Value + +} + +Function Register-DropboxSdkDotnet { + [CmdletBinding()] + param ( + [Parameter(Mandatory)][string]$DbxAppKey, + [Parameter(Mandatory)][string]$DbxAppSecret, + [Parameter(Mandatory)][string]$DbxUserAccessToken, + [System.EnvironmentVariableTarget]$Scope = [System.EnvironmentVariableTarget]::User + ) + + PROCESS { + Set-EnvironmentVariable 'DROPBOX_INTEGRATION_AppKey' $DbxAppKey $Scope + Set-EnvironmentVariable 'DROPBOX_INTEGRATION_appSecret' $DbxAppSecret $Scope + Set-EnvironmentVariable 'DROPBOX_INTEGRATION_userAccessToken' $DbxUserAccessToken $Scope + } +} +Register-DropboxSdkDotnet @PSBoundParameters diff --git a/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/DropboxApiTests.cs b/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/DropboxApiTests.cs index dc87074b6c..14d282f90e 100644 --- a/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/DropboxApiTests.cs +++ b/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/DropboxApiTests.cs @@ -82,10 +82,11 @@ public static void ClassInitialize(TestContext context) userRefreshToken = config["userRefreshToken"]; userAccessToken = config["userAccessToken"]; + userAccessToken = ""; client = new DropboxClient(userAccessToken); var teamToken = config["teamAccessToken"]; - teamClient = new DropboxTeamClient(teamToken); + //teamClient = new DropboxTeamClient(teamToken); appClient = new DropboxAppClient(appKey, appSecret); } @@ -116,7 +117,7 @@ public async Task Initialize() [TestCleanup] public void Cleanup() { - var result = client.Files.ListFolderAsync(TestingPath).Result; + var result = client?.Files.ListFolderAsync(TestingPath).Result; foreach (var entry in result.Entries) { diff --git a/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/settings.json.example b/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/settings.json.example deleted file mode 100644 index dfa53a427f..0000000000 --- a/dropbox-sdk-dotnet/Dropbox.Api.Integration.Tests/settings.json.example +++ /dev/null @@ -1,7 +0,0 @@ -{ - "appKey": "", - "appSecret": "", - "userAccessToken": "", - "userRefreshToken": "", - "teamAccessToken": "" -} diff --git a/dropbox-sdk-dotnet/Dropbox.Api.sln b/dropbox-sdk-dotnet/Dropbox.Api.sln index a8f3c925b8..e09cf4de8d 100644 --- a/dropbox-sdk-dotnet/Dropbox.Api.sln +++ b/dropbox-sdk-dotnet/Dropbox.Api.sln @@ -16,11 +16,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dropbox.Api", "Dropbox.Api\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleBusinessDashboard", "Examples\SimpleBusinessDashboard\SimpleBusinessDashboard.csproj", "{B762DE35-E606-4B57-8860-1A662DF5624C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dropbox.Api.Integration.Tests", "Dropbox.Api.Integration.Tests\Dropbox.Api.Integration.Tests.csproj", "{0FDF769B-43F8-4907-98D5-A413BD9DE62D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dropbox.Api.Integration.Tests", "Dropbox.Api.Integration.Tests\Dropbox.Api.Integration.Tests.csproj", "{0FDF769B-43F8-4907-98D5-A413BD9DE62D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dropbox.Api.Unit.Tests", "Dropbox.Api.Unit.Tests\Dropbox.Api.Unit.Tests.csproj", "{EECFD4AD-B7DF-4AA7-B067-C6DA4139CB46}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OauthTest", "Examples\OauthBasic\OauthTest.csproj", "{AC53CD8C-710C-4019-8B62-F819F0D6EF38}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dropbox.Api.Unit.Tests", "Dropbox.Api.Unit.Tests\Dropbox.Api.Unit.Tests.csproj", "{EECFD4AD-B7DF-4AA7-B067-C6DA4139CB46}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthPKCE", "Examples\OAuthPKCE\OAuthPKCE.csproj", "{1DA8B62F-4AB9-4514-BE94-7D86BA4C130D}" EndProject @@ -29,11 +27,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OauthBasic", "Examples\OauthBasic\OauthBasic.csproj", "{7839C19C-D817-4D7E-AA66-919042050167}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleTestCore", "Examples\SimpleTestCore\SimpleTestCore.csproj", "{649E39D9-C248-4C81-8486-4EC068DE0FD2}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Examples\UniversalDemo\UniversalDemo\UniversalDemo.Shared\UniversalDemo.Shared.projitems*{51d9899c-31d6-4c22-b894-e9498cb90577}*SharedItemsImports = 4 Examples\UniversalDemo\UniversalDemo\UniversalDemo.Shared\UniversalDemo.Shared.projitems*{680e8f6f-aa67-4912-ae20-f89e482dc2cd}*SharedItemsImports = 4 - Examples\UniversalDemo\UniversalDemo\UniversalDemo.Shared\UniversalDemo.Shared.projitems*{6bcc4215-b726-4b37-a932-4dca74a1d465}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -156,22 +157,6 @@ Global {EECFD4AD-B7DF-4AA7-B067-C6DA4139CB46}.Release|ARM.ActiveCfg = Release|Any CPU {EECFD4AD-B7DF-4AA7-B067-C6DA4139CB46}.Release|x64.ActiveCfg = Release|Any CPU {EECFD4AD-B7DF-4AA7-B067-C6DA4139CB46}.Release|x86.ActiveCfg = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|ARM.ActiveCfg = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|ARM.Build.0 = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|x64.ActiveCfg = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|x64.Build.0 = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|x86.ActiveCfg = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Debug|x86.Build.0 = Debug|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|Any CPU.Build.0 = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|ARM.ActiveCfg = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|ARM.Build.0 = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|x64.ActiveCfg = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|x64.Build.0 = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|x86.ActiveCfg = Release|Any CPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38}.Release|x86.Build.0 = Release|Any CPU {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D}.Debug|Any CPU.Build.0 = Debug|Any CPU {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -188,6 +173,38 @@ Global {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D}.Release|x64.Build.0 = Release|Any CPU {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D}.Release|x86.ActiveCfg = Release|Any CPU {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D}.Release|x86.Build.0 = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|ARM.Build.0 = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|x64.ActiveCfg = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|x64.Build.0 = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|x86.ActiveCfg = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Debug|x86.Build.0 = Debug|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|Any CPU.Build.0 = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|ARM.ActiveCfg = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|ARM.Build.0 = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|x64.ActiveCfg = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|x64.Build.0 = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|x86.ActiveCfg = Release|Any CPU + {7839C19C-D817-4D7E-AA66-919042050167}.Release|x86.Build.0 = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|ARM.Build.0 = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|x64.ActiveCfg = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|x64.Build.0 = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|x86.ActiveCfg = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Debug|x86.Build.0 = Debug|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|Any CPU.Build.0 = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|ARM.ActiveCfg = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|ARM.Build.0 = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|x64.ActiveCfg = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|x64.Build.0 = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|x86.ActiveCfg = Release|Any CPU + {649E39D9-C248-4C81-8486-4EC068DE0FD2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/dropbox-sdk-dotnet/Dropbox.Api/Dropbox.Api.csproj b/dropbox-sdk-dotnet/Dropbox.Api/Dropbox.Api.csproj index b249ce1ae5..b3ca6c193e 100644 --- a/dropbox-sdk-dotnet/Dropbox.Api/Dropbox.Api.csproj +++ b/dropbox-sdk-dotnet/Dropbox.Api/Dropbox.Api.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.1 true ../stylecop.ruleset diff --git a/dropbox-sdk-dotnet/Examples/OAuthPKCE/OAuthPKCE.csproj b/dropbox-sdk-dotnet/Examples/OAuthPKCE/OAuthPKCE.csproj index e34578f2c8..8b138026c0 100644 --- a/dropbox-sdk-dotnet/Examples/OAuthPKCE/OAuthPKCE.csproj +++ b/dropbox-sdk-dotnet/Examples/OAuthPKCE/OAuthPKCE.csproj @@ -1,108 +1,26 @@ - - - + + - Debug - AnyCPU - {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D} Exe - Properties - OauthPKCE - OauthPKCE - v4.5 - 512 - 8bfaafb2 - ..\ - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + net5.0 + + + + + - - - - True - True - Settings.settings - + + - - - + + PreserveNewest + + SettingsSingleFileGenerator Settings.Designer.cs - - - False - ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll - - - False - ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll - - - False - ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll - - - - - - - - False - ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - - - False - ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - - - - - - - - {68180b54-4724-4cd1-baa6-ee7bc309797c} - Dropbox.Api - - - - - Always - - - - - - - - - - \ No newline at end of file + + diff --git a/dropbox-sdk-dotnet/Examples/OauthBasic/OauthTest.csproj b/dropbox-sdk-dotnet/Examples/OAuthPKCE/OAuthPKCE.csproj.old similarity index 97% rename from dropbox-sdk-dotnet/Examples/OauthBasic/OauthTest.csproj rename to dropbox-sdk-dotnet/Examples/OAuthPKCE/OAuthPKCE.csproj.old index 68bbfb141e..e34578f2c8 100644 --- a/dropbox-sdk-dotnet/Examples/OauthBasic/OauthTest.csproj +++ b/dropbox-sdk-dotnet/Examples/OAuthPKCE/OAuthPKCE.csproj.old @@ -4,11 +4,11 @@ Debug AnyCPU - {AC53CD8C-710C-4019-8B62-F819F0D6EF38} + {1DA8B62F-4AB9-4514-BE94-7D86BA4C130D} Exe Properties - OauthTest - OauthTest + OauthPKCE + OauthPKCE v4.5 512 8bfaafb2 diff --git a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs b/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs index 333805fb3f..84ca08c78e 100644 --- a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs +++ b/dropbox-sdk-dotnet/Examples/OAuthPKCE/Program.cs @@ -1,6 +1,7 @@ namespace OauthPKCE { using System; + using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -9,12 +10,10 @@ namespace OauthPKCE using System.Threading.Tasks; using Dropbox.Api; + using OAuthPKCE; partial class Program { - // Add an ApiKey (from https://www.dropbox.com/developers/apps) here - private const string ApiKey = "XXXXXXXXXXXXXXX"; - // This loopback host is for demo purpose. If this port is not // available on your machine you need to update this URL with an unused port. private const string LoopbackHost = "http://127.0.0.1:52475/"; @@ -35,38 +34,26 @@ partial class Program private static extern bool SetForegroundWindow(IntPtr hWnd); [STAThread] - static int Main(string[] args) + static async Task Main() { - var instance = new Program(); - try - { - Console.WriteLine("Example OAuth PKCE Application"); - var task = Task.Run((Func>)instance.Run); - - task.Wait(); - - return task.Result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw e; - } + return await Task.Run(new Program().Run); } private async Task Run() { + Console.WriteLine("Example OAuth PKCE Application"); DropboxCertHelper.InitializeCertPinning(); - var uid = await this.AcquireAccessToken(null, IncludeGrantedScopes.None); - if (string.IsNullOrEmpty(uid)) + string accessToken = await GetOAuthTokens(null, IncludeGrantedScopes.None); + if (string.IsNullOrEmpty(accessToken)) { return 1; } // Specify socket level timeout which decides maximum waiting time when no bytes are // received by the socket. - var httpClient = new HttpClient(new WebRequestHandler { ReadWriteTimeout = 10 * 1000 }) + using HttpClientHandler httpClientHandler = new HttpClientHandler(); + using var httpClient = new HttpClient(httpClientHandler) { // Specify request level timeout which decides maximum time that can be spent on // download/upload files. @@ -80,12 +67,9 @@ private async Task Run() HttpClient = httpClient }; - var client = new DropboxClient(Settings.Default.RefreshToken, ApiKey, config); + var client = new DropboxClient(Settings.Default.RefreshToken, Settings.Default.ApiKey, config); + await GetCurrentAccount(client); // This call should succeed since the correct scope has been acquired - // This call should succeed since the correct scope has been acquired - await GetCurrentAccount(client); - - Console.WriteLine("Oauth PKCE Test Complete!"); Console.WriteLine("Exit with any key"); Console.ReadKey(); } @@ -103,6 +87,7 @@ private async Task Run() return 0; } + /// /// Handles the redirect from Dropbox server. Because we are using token flow, the local /// http server cannot directly receive the URL fragment. We need to return a HTML page with @@ -148,22 +133,21 @@ private async Task HandleJSRedirect(HttpListener http) context = await http.GetContextAsync(); } - var redirectUri = new Uri(context.Request.QueryString["url_with_fragment"]); - - return redirectUri; + return new Uri(context.Request.QueryString["url_with_fragment"]); } /// - /// Acquires a dropbox access token and saves it to the default settings for the app. + /// Acquires a dropbox OAuth tokens and saves them to the default settings for the app. /// - /// This fetches the access token from the applications settings, if it is not found there + /// This fetches the OAuth tokens from the applications settings, if it is not found there /// (or if the user chooses to reset the settings) then the UI in is /// displayed to authorize the user. /// /// - /// A valid uid if a token was acquired or null. - private async Task AcquireAccessToken(string[] scopeList, IncludeGrantedScopes includeGrantedScopes) + /// A valid access token if successful otherwise null. + private async Task GetOAuthTokens(string[] scopeList, IncludeGrantedScopes includeGrantedScopes) { + Settings.Default.Upgrade(); Console.Write("Reset settings (Y/N) "); if (Console.ReadKey().Key == ConsoleKey.Y) { @@ -171,58 +155,66 @@ private async Task AcquireAccessToken(string[] scopeList, IncludeGranted } Console.WriteLine(); - var accessToken = Settings.Default.AccessToken; - var refreshToken = Settings.Default.RefreshToken; - - if (string.IsNullOrEmpty(accessToken)) + if (string.IsNullOrEmpty(Settings.Default.AccessToken)) { + string apiKey = GetApiKey(); + + using var http = new HttpListener(); try { - Console.WriteLine("Waiting for credentials."); - var state = Guid.NewGuid().ToString("N"); + string state = Guid.NewGuid().ToString("N"); var OAuthFlow = new PKCEOAuthFlow(); - var authorizeUri = OAuthFlow.GetAuthorizeUri(OAuthResponseType.Code, ApiKey, RedirectUri.ToString(), state: state, tokenAccessType: TokenAccessType.Offline, scopeList: scopeList, includeGrantedScopes: includeGrantedScopes); - var http = new HttpListener(); + var authorizeUri = OAuthFlow.GetAuthorizeUri( + OAuthResponseType.Code, apiKey, RedirectUri.ToString(), + state: state, tokenAccessType: TokenAccessType.Offline, + scopeList: scopeList, includeGrantedScopes: includeGrantedScopes); + http.Prefixes.Add(LoopbackHost); http.Start(); - System.Diagnostics.Process.Start(authorizeUri.ToString()); + // Use StartInfo to ensure default browser launches. + ProcessStartInfo startInfo = new ProcessStartInfo( + authorizeUri.ToString()) + { UseShellExecute = true }; + + try + { + // open browser for authentication + Console.WriteLine("Waiting for credentials and authorization."); + Process.Start(startInfo); + } + catch (Exception) + { + Console.WriteLine("An unexpected error occured while opening the browser."); + } // Handle OAuth redirect and send URL fragment to local server using JS. await HandleOAuth2Redirect(http); // Handle redirect from JS and process OAuth response. - var redirectUri = await HandleJSRedirect(http); + Uri redirectUri = await HandleJSRedirect(http); - Console.WriteLine("Exchanging code for token"); - var tokenResult = await OAuthFlow.ProcessCodeFlowAsync(redirectUri, ApiKey, RedirectUri.ToString(), state); - Console.WriteLine("Finished Exchanging Code for Token"); - // Bring console window to the front. - SetForegroundWindow(GetConsoleWindow()); - accessToken = tokenResult.AccessToken; - refreshToken = tokenResult.RefreshToken; - var uid = tokenResult.Uid; - Console.WriteLine("Uid: {0}", uid); - Console.WriteLine("AccessToken: {0}", accessToken); - if (tokenResult.RefreshToken != null) - { - Console.WriteLine("RefreshToken: {0}", refreshToken); - Settings.Default.RefreshToken = refreshToken; - } - if (tokenResult.ExpiresAt != null) - { - Console.WriteLine("ExpiresAt: {0}", tokenResult.ExpiresAt); - } - if (tokenResult.ScopeList != null) + http.Stop(); + + // Exchanging code for token + var result = await OAuthFlow.ProcessCodeFlowAsync( + redirectUri, apiKey, RedirectUri.ToString(), state); + if (result.State != state) { - Console.WriteLine("Scopes: {0}", String.Join(" ", tokenResult.ScopeList)); + // NOTE: Rightly or wrongly?, state is not returned or else + // we would return null here. + // See issue https://github.com/dropbox/dropbox-sdk-dotnet/issues/248 + Console.WriteLine("The state in the response doesn't match the state in the request."); } - Settings.Default.AccessToken = accessToken; - Settings.Default.Uid = uid; - Settings.Default.Save(); - http.Stop(); - return uid; + Console.WriteLine("OAuth token aquire complete"); + + // Bring console window to the front. + SetForegroundWindow(GetConsoleWindow()); + + DisplayOAuthResult(result); + + UpdateSettings(result); } catch (Exception e) { @@ -231,7 +223,60 @@ private async Task AcquireAccessToken(string[] scopeList, IncludeGranted } } - return null; + return Settings.Default.AccessToken; + } + + private static void UpdateSettings(OAuth2Response result) + { + // Foreach Settting, save off the value retrieved from the result. + foreach (System.Configuration.SettingsProperty item in Settings.Default.Properties) + { + if (typeof(OAuth2Response).GetProperty(item.Name) is System.Reflection.PropertyInfo property) + { + Settings.Default[item.Name] = property.GetValue(result); + } + } + + Settings.Default.Save(); + Settings.Default.Reload(); + } + + private static void DisplayOAuthResult(OAuth2Response result) + { + Console.WriteLine("OAuth Result:"); + Console.WriteLine("\tUid: {0}", result.Uid); + Console.WriteLine("\tAccessToken: {0}", result.AccessToken); + Console.WriteLine("\tRefreshToken: {0}", result.RefreshToken); + Console.WriteLine("\tExpiresAt: {0}", result.ExpiresAt); + Console.WriteLine("\tScopes: {0}", string.Join(" ", result.ScopeList?? new string[0])); + } + + /// + /// Retrieve the ApiKey from the user + /// + /// Return the ApiKey specified by the user + private static string GetApiKey() + { + string apiKey = Settings.Default.ApiKey; + + while (string.IsNullOrWhiteSpace(apiKey)) + { + Console.WriteLine("Create a Dropbox App at https://www.dropbox.com/developers/apps."); + Console.Write("Enter the API Key (or 'Quit' to exit): "); + apiKey = Console.ReadLine(); + if (apiKey.ToLower() == "quit") + { + Console.WriteLine("The API Key is required to connect to Dropbox."); + apiKey = null; + break; + } + else + { + Settings.Default.ApiKey = apiKey; + } + } + + return string.IsNullOrWhiteSpace(apiKey) ? null : apiKey; } /// @@ -242,41 +287,33 @@ private async Task AcquireAccessToken(string[] scopeList, IncludeGranted /// /// The Dropbox client. /// An asynchronous task. - private async Task GetCurrentAccount(DropboxClient client) + static private async Task GetCurrentAccount(DropboxClient client) { - try + Console.WriteLine("Current Account:"); + var full = await client.Users.GetCurrentAccountAsync(); + + Console.WriteLine("Account id : {0}", full.AccountId); + Console.WriteLine("Country : {0}", full.Country); + Console.WriteLine("Email : {0}", full.Email); + Console.WriteLine("Is paired : {0}", full.IsPaired ? "Yes" : "No"); + Console.WriteLine("Locale : {0}", full.Locale); + Console.WriteLine("Name"); + Console.WriteLine(" Display : {0}", full.Name.DisplayName); + Console.WriteLine(" Familiar : {0}", full.Name.FamiliarName); + Console.WriteLine(" Given : {0}", full.Name.GivenName); + Console.WriteLine(" Surname : {0}", full.Name.Surname); + Console.WriteLine("Referral link : {0}", full.ReferralLink); + + if (full.Team != null) { - Console.WriteLine("Current Account:"); - var full = await client.Users.GetCurrentAccountAsync(); - - Console.WriteLine("Account id : {0}", full.AccountId); - Console.WriteLine("Country : {0}", full.Country); - Console.WriteLine("Email : {0}", full.Email); - Console.WriteLine("Is paired : {0}", full.IsPaired ? "Yes" : "No"); - Console.WriteLine("Locale : {0}", full.Locale); - Console.WriteLine("Name"); - Console.WriteLine(" Display : {0}", full.Name.DisplayName); - Console.WriteLine(" Familiar : {0}", full.Name.FamiliarName); - Console.WriteLine(" Given : {0}", full.Name.GivenName); - Console.WriteLine(" Surname : {0}", full.Name.Surname); - Console.WriteLine("Referral link : {0}", full.ReferralLink); - - if (full.Team != null) - { - Console.WriteLine("Team"); - Console.WriteLine(" Id : {0}", full.Team.Id); - Console.WriteLine(" Name : {0}", full.Team.Name); - } - else - { - Console.WriteLine("Team - None"); - } + Console.WriteLine("Team"); + Console.WriteLine(" Id : {0}", full.Team.Id); + Console.WriteLine(" Name : {0}", full.Team.Name); } - catch (Exception e) + else { - throw e; + Console.WriteLine("Team - None"); } - } } } diff --git a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Properties/AssemblyInfo.cs b/dropbox-sdk-dotnet/Examples/OAuthPKCE/Properties/AssemblyInfo.cs deleted file mode 100644 index a55dd2e08c..0000000000 --- a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OAuthTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("OAuthTest")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("d379c634-a147-425a-94bb-ff73c54a9aa3")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Settings.Designer.cs b/dropbox-sdk-dotnet/Examples/OAuthPKCE/Settings.Designer.cs index eb1dfbf485..bc8389cec1 100644 --- a/dropbox-sdk-dotnet/Examples/OAuthPKCE/Settings.Designer.cs +++ b/dropbox-sdk-dotnet/Examples/OAuthPKCE/Settings.Designer.cs @@ -8,11 +8,11 @@ // //------------------------------------------------------------------------------ -namespace OauthPKCE { +namespace OAuthPKCE { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -22,16 +22,31 @@ public static Settings Default { return defaultInstance; } } - + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ApiKey + { + get + { + return ((string)(this[nameof(ApiKey)])); + } + set + { + this[nameof(ApiKey)] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("")] public string RefreshToken { get { - return ((string)(this["RefreshToken"])); + return ((string)this[nameof(RefreshToken)]); } set { - this["RefreshToken"] = value; + this[nameof(RefreshToken)] = value; } } @@ -40,10 +55,10 @@ public string RefreshToken { [global::System.Configuration.DefaultSettingValueAttribute("")] public string AccessToken { get { - return ((string)(this["AccessToken"])); + return ((string)(this[nameof(AccessToken)])); } set { - this["AccessToken"] = value; + this[nameof(AccessToken)] = value; } } diff --git a/dropbox-sdk-dotnet/Examples/OauthBasic/App.config b/dropbox-sdk-dotnet/Examples/OauthBasic/App.config index 4ef02eefa7..f996194526 100644 --- a/dropbox-sdk-dotnet/Examples/OauthBasic/App.config +++ b/dropbox-sdk-dotnet/Examples/OauthBasic/App.config @@ -6,7 +6,7 @@ - + diff --git a/dropbox-sdk-dotnet/Examples/OauthBasic/OauthBasic.csproj b/dropbox-sdk-dotnet/Examples/OauthBasic/OauthBasic.csproj new file mode 100644 index 0000000000..8e520bf47e --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/OauthBasic/OauthBasic.csproj @@ -0,0 +1,21 @@ + + + + Exe + net5.0 + + + + + + + + + + + + + + + + diff --git a/dropbox-sdk-dotnet/Examples/OauthBasic/OauthTest.csproj.old b/dropbox-sdk-dotnet/Examples/OauthBasic/OauthTest.csproj.old new file mode 100644 index 0000000000..457fd8c3a2 --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/OauthBasic/OauthTest.csproj.old @@ -0,0 +1,111 @@ + + + + + Debug + AnyCPU + {AC53CD8C-710C-4019-8B62-F819F0D6EF38} + Exe + Properties + OauthTest + OauthTest + v4.6.1 + 512 + + + ..\ + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + True + True + Settings.settings + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + False + ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll + + + False + ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + False + ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + + + + + + False + ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll + + + False + ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll + + + + + + + + {68180b54-4724-4cd1-baa6-ee7bc309797c} + Dropbox.Api + + + + + Always + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/dropbox-sdk-dotnet/Examples/OauthBasic/Program.cs b/dropbox-sdk-dotnet/Examples/OauthBasic/Program.cs index ab5f3f2897..9b7f0bbcd0 100644 --- a/dropbox-sdk-dotnet/Examples/OauthBasic/Program.cs +++ b/dropbox-sdk-dotnet/Examples/OauthBasic/Program.cs @@ -13,10 +13,10 @@ namespace OauthTest partial class Program { // Add an ApiKey (from https://www.dropbox.com/developers/apps) here - private const string ApiKey = "XXXXXXXXXXXXXXX"; + private const string ApiKey = "XXXXXXXXXXXXXXXX"; // Add an ApiSecret (from https://www.dropbox.com/developers/apps) here - private const string ApiSecret = "XXXXXXXXXXXXXXX"; + private const string ApiSecret = "XXXXXXXXXXXXXXXX"; // This loopback host is for demo purpose. If this port is not // available on your machine you need to update this URL with an unused port. @@ -70,7 +70,7 @@ private async Task Run() // Specify socket level timeout which decides maximum waiting time when no bytes are // received by the socket. - var httpClient = new HttpClient(new WebRequestHandler { ReadWriteTimeout = 10 * 1000 }) + var httpClient = new HttpClient(new HttpClientHandler()) { // Specify request level timeout which decides maximum time that can be spent on // download/upload files. @@ -84,7 +84,30 @@ private async Task Run() HttpClient = httpClient }; - var client = new DropboxClient(Settings.Default.AccessToken, Settings.Default.RefreshToken, ApiKey, ApiSecret, config); + string apiKey = Settings.Default.ApiKey; + while (string.IsNullOrWhiteSpace(apiKey)) + { + Console.WriteLine("Create a Dropbox App at https://www.dropbox.com/developers/apps."); + Console.Write("Enter the API Key (or 'Quit' to exit): "); + apiKey = Console.ReadLine(); + if (apiKey.ToLower() == "quit") + { + Console.WriteLine("The API Key is required to connect to Dropbox."); + apiKey = ""; + break; + } + else + { + Settings.Default.ApiKey = apiKey; + } + } + + var accessToken = Settings.Default.AccessToken; + + if (string.IsNullOrEmpty(accessToken) && !string.IsNullOrWhiteSpace(apiKey)) + + + var client = new DropboxClient(Settings.Default.AccessToken, Settings.Default.RefreshToken, ApiKey, ApiSecret, config); // This call should succeed since the correct scope has been acquired await GetCurrentAccount(client); diff --git a/dropbox-sdk-dotnet/Examples/OauthBasic/Settings.Designer.cs b/dropbox-sdk-dotnet/Examples/OauthBasic/Settings.Designer.cs index 6a6c4c3b3b..92fa820584 100644 --- a/dropbox-sdk-dotnet/Examples/OauthBasic/Settings.Designer.cs +++ b/dropbox-sdk-dotnet/Examples/OauthBasic/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace OauthTest { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/dropbox-sdk-dotnet/Examples/OauthBasic/packages.config b/dropbox-sdk-dotnet/Examples/OauthBasic/packages.config index 6671d04079..815cd0070a 100644 --- a/dropbox-sdk-dotnet/Examples/OauthBasic/packages.config +++ b/dropbox-sdk-dotnet/Examples/OauthBasic/packages.config @@ -1,7 +1,7 @@ - + - + \ No newline at end of file diff --git a/dropbox-sdk-dotnet/Examples/SimpleTest/App.config b/dropbox-sdk-dotnet/Examples/SimpleTest/App.config index feaec551c4..212b20bf1e 100644 --- a/dropbox-sdk-dotnet/Examples/SimpleTest/App.config +++ b/dropbox-sdk-dotnet/Examples/SimpleTest/App.config @@ -6,15 +6,18 @@ - + - + - + + + + diff --git a/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs b/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs index 78be8c6bb0..ea72192400 100644 --- a/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs +++ b/dropbox-sdk-dotnet/Examples/SimpleTest/Program.cs @@ -1,10 +1,12 @@ namespace SimpleTest { using System; + using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; + using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -15,9 +17,6 @@ namespace SimpleTest partial class Program { - // Add an ApiKey (from https://www.dropbox.com/developers/apps) here - private const string ApiKey = "XXXXXXXXXXXXXXX"; - // This loopback host is for demo purpose. If this port is not // available on your machine you need to update this URL with an unused port. private const string LoopbackHost = "http://127.0.0.1:52475/"; @@ -38,30 +37,17 @@ partial class Program private static extern bool SetForegroundWindow(IntPtr hWnd); [STAThread] - static int Main(string[] args) + static async Task Main() { - Console.WriteLine("SimpleTest"); - var instance = new Program(); - try - { - var task = Task.Run((Func>)instance.Run); - - task.Wait(); - - return task.Result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw e; - } + return await Task.Run(new Program().Run); } private async Task Run() { + Console.WriteLine(nameof(SimpleTest)); DropboxCertHelper.InitializeCertPinning(); - var accessToken = await this.GetAccessToken(); + string accessToken = await GetOAuthTokens(); if (string.IsNullOrEmpty(accessToken)) { return 1; @@ -69,7 +55,8 @@ private async Task Run() // Specify socket level timeout which decides maximum waiting time when no bytes are // received by the socket. - var httpClient = new HttpClient(new WebRequestHandler { ReadWriteTimeout = 10 * 1000 }) + using HttpClientHandler httpClientHandler = new HttpClientHandler(); + using var httpClient = new HttpClient(httpClientHandler) { // Specify request level timeout which decides maximum time that can be spent on // download/upload files. @@ -93,6 +80,9 @@ private async Task Run() var client = new DropboxTeamClient(accessToken, userAgent: "SimpleTeamTestApp", httpClient: httpClient); await RunTeamTests(client); */ + + Console.WriteLine("Exit with any key"); + Console.ReadKey(); } catch (HttpException e) { @@ -108,53 +98,6 @@ private async Task Run() return 0; } - /// - /// Run tests for user-level operations. - /// - /// The Dropbox client. - /// An asynchronous task. - private async Task RunUserTests(DropboxClient client) - { - await GetCurrentAccount(client); - - var path = "/DotNetApi/Help"; - var folder = await CreateFolder(client, path); - var list = await ListFolder(client, path); - - var firstFile = list.Entries.FirstOrDefault(i => i.IsFile); - if (firstFile != null) - { - await Download(client, path, firstFile.AsFile); - } - - var pathInTeamSpace = "/Test"; - await ListFolderInTeamSpace(client, pathInTeamSpace); - - await Upload(client, path, "Test.txt", "This is a text file"); - - await ChunkUpload(client, path, "Binary"); - } - - /// - /// Run tests for team-level operations. - /// - /// The Dropbox client. - /// An asynchronous task. - private async Task RunTeamTests(DropboxTeamClient client) - { - var members = await client.Team.MembersListAsync(); - - var member = members.Members.FirstOrDefault(); - - if (member != null) - { - // A team client can perform action on a team member's behalf. To do this, - // just pass in team member id in to AsMember function which returns a user client. - // This client will operates on this team member's Dropbox. - var userClient = client.AsMember(member.Profile.TeamMemberId); - await RunUserTests(userClient); - } - } /// /// Handles the redirect from Dropbox server. Because we are using token flow, the local @@ -191,7 +134,7 @@ private async Task HandleOAuth2Redirect(HttpListener http) /// /// The http listener. /// The - private async Task HandleJSRedirect(HttpListener http) + private async Task HandleJSRedirect(HttpListener http) { var context = await http.GetContextAsync(); @@ -201,24 +144,21 @@ private async Task HandleJSRedirect(HttpListener http) context = await http.GetContextAsync(); } - var redirectUri = new Uri(context.Request.QueryString["url_with_fragment"]); - - var result = DropboxOAuth2Helper.ParseTokenFragment(redirectUri); - - return result; + return new Uri(context.Request.QueryString["url_with_fragment"]); } /// - /// Gets the dropbox access token. + /// Acquires a dropbox OAuth tokens and saves them to the default settings for the app. /// - /// This fetches the access token from the applications settings, if it is not found there + /// This fetches the OAuth tokens from the applications settings, if it is not found there /// (or if the user chooses to reset the settings) then the UI in is /// displayed to authorize the user. /// /// - /// A valid access token or null. - private async Task GetAccessToken() + /// A valid access token if successful otherwise null. + private async Task GetOAuthTokens() { + Settings.Default.Upgrade(); Console.Write("Reset settings (Y/N) "); if (Console.ReadKey().Key == ConsoleKey.Y) { @@ -226,47 +166,56 @@ private async Task GetAccessToken() } Console.WriteLine(); - var accessToken = Settings.Default.AccessToken; - - if (string.IsNullOrEmpty(accessToken)) + if (string.IsNullOrEmpty(Settings.Default.AccessToken)) { + string apiKey = GetApiKey(); + using var http = new HttpListener(); try { - Console.WriteLine("Waiting for credentials."); - var state = Guid.NewGuid().ToString("N"); - var authorizeUri = DropboxOAuth2Helper.GetAuthorizeUri(OAuthResponseType.Token, ApiKey, RedirectUri, state: state); - var http = new HttpListener(); + string state = Guid.NewGuid().ToString("N"); + var authorizeUri = DropboxOAuth2Helper.GetAuthorizeUri( + OAuthResponseType.Token, apiKey, RedirectUri, state: state); + http.Prefixes.Add(LoopbackHost); http.Start(); - System.Diagnostics.Process.Start(authorizeUri.ToString()); + // Use StartInfo to ensure default browser launches. + ProcessStartInfo startInfo = new ProcessStartInfo( + authorizeUri.ToString()) { UseShellExecute = true }; + + try + { + // open browser for authentication + Console.WriteLine("Waiting for credentials and authorization."); + Process.Start(startInfo); + } + catch (Exception) + { + Console.WriteLine("An unexpected error occured while opening the browser."); + } // Handle OAuth redirect and send URL fragment to local server using JS. await HandleOAuth2Redirect(http); // Handle redirect from JS and process OAuth response. - var result = await HandleJSRedirect(http); + Uri redirectUri = await HandleJSRedirect(http); + http.Stop(); + OAuth2Response result = DropboxOAuth2Helper.ParseTokenFragment(redirectUri); if (result.State != state) { // The state in the response doesn't match the state in the request. return null; } - - Console.WriteLine("and back..."); + Console.WriteLine("OAuth token aquire complete"); // Bring console window to the front. SetForegroundWindow(GetConsoleWindow()); - accessToken = result.AccessToken; - var uid = result.Uid; - Console.WriteLine("Uid: {0}", uid); - - Settings.Default.AccessToken = accessToken; - Settings.Default.Uid = uid; + DisplayOAuthResult(result); - Settings.Default.Save(); + UpdateSettings(result); } catch (Exception e) { @@ -275,7 +224,60 @@ private async Task GetAccessToken() } } - return accessToken; + return Settings.Default.AccessToken; + } + + private static void UpdateSettings(OAuth2Response result) + { + // Foreach Settting, save off the value retrieved from the result. + foreach (System.Configuration.SettingsProperty item in Settings.Default.Properties) + { + if (typeof(OAuth2Response).GetProperty(item.Name) is PropertyInfo property) + { + Settings.Default[item.Name] = property.GetValue(result); + } + } + + Settings.Default.Save(); + Settings.Default.Reload(); + } + + private static void DisplayOAuthResult(OAuth2Response result) + { + Console.WriteLine("OAuth Result:"); + Console.WriteLine("\tUid: {0}", result.Uid); + Console.WriteLine("\tAccessToken: {0}", result.AccessToken); + Console.WriteLine("\tRefreshToken: {0}", result.RefreshToken); + Console.WriteLine("\tExpiresAt: {0}", result.ExpiresAt); + Console.WriteLine("\tScopes: {0}", string.Join(" ", result.ScopeList?? new string[0])); + } + + /// + /// Retrieve the ApiKey from the user + /// + /// Return the ApiKey specified by the user + private static string GetApiKey() + { + string apiKey = Settings.Default.ApiKey; + + while (string.IsNullOrWhiteSpace(apiKey)) + { + Console.WriteLine("Create a Dropbox App at https://www.dropbox.com/developers/apps."); + Console.Write("Enter the API Key (or 'Quit' to exit): "); + apiKey = Console.ReadLine(); + if (apiKey.ToLower() == "quit") + { + Console.WriteLine("The API Key is required to connect to Dropbox."); + apiKey = null; + break; + } + else + { + Settings.Default.ApiKey = apiKey; + } + } + + return string.IsNullOrWhiteSpace(apiKey) ? null : apiKey; } /// @@ -286,8 +288,9 @@ private async Task GetAccessToken() /// /// The Dropbox client. /// An asynchronous task. - private async Task GetCurrentAccount(DropboxClient client) + static private async Task GetCurrentAccount(DropboxClient client) { + Console.WriteLine("Current Account:"); var full = await client.Users.GetCurrentAccountAsync(); Console.WriteLine("Account id : {0}", full.AccountId); @@ -314,6 +317,97 @@ private async Task GetCurrentAccount(DropboxClient client) } } + /// + /// Run tests for user-level operations. + /// + /// The Dropbox client. + /// An asynchronous task. + private async Task RunUserTests(DropboxClient client) + { + + await GetCurrentAccount(client); + + var path = "/DotNetApi/Help"; + await DeleteFolder(client, path); // Removes items left older from the previous test run. + + var folder = await CreateFolder(client, path); + + var pathInTeamSpace = "/Test"; + await ListFolderInTeamSpace(client, pathInTeamSpace); + + await Upload(client, path, "Test.txt", "This is a text file"); + + await ChunkUpload(client, path, "Binary"); + + ListFolderResult list = await ListFolder(client, path); + + Metadata firstFile = list.Entries.FirstOrDefault(i => i.IsFile); + if (firstFile != null) + { + await Download(client, path, firstFile.AsFile); + } + + await DeleteFolder(client, path); + } + + /// + /// Run tests for team-level operations. + /// + /// The Dropbox client. + /// An asynchronous task. + private async Task RunTeamTests(DropboxTeamClient client) + { + var members = await client.Team.MembersListAsync(); + + var member = members.Members.FirstOrDefault(); + + if (member != null) + { + // A team client can perform action on a team member's behalf. To do this, + // just pass in team member id in to AsMember function which returns a user client. + // This client will operates on this team member's Dropbox. + var userClient = client.AsMember(member.Profile.TeamMemberId); + await RunUserTests(userClient); + } + } + + /// + /// Delete the specified folder including any files within the folder + /// + /// The dropbox client object. + /// The path to the target folder to delete. + /// + static private async Task PathExists(DropboxClient client, string path) + { + try + { + await client.Files.GetMetadataAsync(path); + return true; + } + catch (DropboxException exception) when (exception.Message.StartsWith("path/not_found/")) + { + return false; + } + } + + /// + /// Delete the specified folder including any files within the folder + /// + /// The dropbox client object. + /// The path to the target folder to delete. + /// + static private async Task DeleteFolder(DropboxClient client, string path) + { + if(await PathExists(client, path)) + { + Console.WriteLine("--- Deleting Folder ---"); + Metadata metadata = await client.Files.DeleteAsync(path); + Console.WriteLine($"Deleted {metadata.PathLower}"); + return metadata; + } + return null; + } + /// /// Creates the specified folder. /// diff --git a/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.Designer.cs b/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.Designer.cs index 8aafbc3314..d4c9867637 100644 --- a/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.Designer.cs +++ b/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.Designer.cs @@ -1,7 +1,7 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34014 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ namespace SimpleTest { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -46,5 +46,17 @@ public string Uid { this["Uid"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ApiKey { + get { + return ((string)(this["ApiKey"])); + } + set { + this["ApiKey"] = value; + } + } } } diff --git a/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.settings b/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.settings index e9e7b06a15..3c1f69809f 100644 --- a/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.settings +++ b/dropbox-sdk-dotnet/Examples/SimpleTest/Settings.settings @@ -8,5 +8,8 @@ + + + \ No newline at end of file diff --git a/dropbox-sdk-dotnet/Examples/SimpleTest/SimpleTest.csproj b/dropbox-sdk-dotnet/Examples/SimpleTest/SimpleTest.csproj index 833cf22901..dff3cd9fa2 100644 --- a/dropbox-sdk-dotnet/Examples/SimpleTest/SimpleTest.csproj +++ b/dropbox-sdk-dotnet/Examples/SimpleTest/SimpleTest.csproj @@ -1,108 +1,35 @@ - - - + + - Debug - AnyCPU - {33A16535-A722-4679-BD9F-A78576988519} Exe - Properties - SimpleTest - SimpleTest - v4.5 - 512 - 8bfaafb2 - ..\ - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + net5.0 + - - - - True + + + + + + + + + + True + True Settings.settings + - - - + + PreserveNewest + + SettingsSingleFileGenerator Settings.Designer.cs - - - False - ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll - - - False - ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll - - - False - ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll - - - - - - - - False - ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - - - False - ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - - - - - - - - {68180b54-4724-4cd1-baa6-ee7bc309797c} - Dropbox.Api - - - - - Always - - - - - - - - - - \ No newline at end of file + + diff --git a/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/App.config b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/App.config new file mode 100644 index 0000000000..fdace44199 --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/App.config @@ -0,0 +1,29 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Program.cs b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Program.cs new file mode 100644 index 0000000000..a0cd0975ca --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Program.cs @@ -0,0 +1,555 @@ +namespace SimpleTest +{ + using System; + using System.IO; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Runtime.InteropServices; + using System.Threading.Tasks; + + using Dropbox.Api; + using Dropbox.Api.Common; + using Dropbox.Api.Files; + using Dropbox.Api.Team; + + partial class Program + { + // Add an ApiKey (from https://www.dropbox.com/developers/apps) here + private const string ApiKey = "XXXXXXXXXXXXXXX"; + + // This loopback host is for demo purpose. If this port is not + // available on your machine you need to update this URL with an unused port. + private const string LoopbackHost = "http://127.0.0.1:52475/"; + + // URL to receive OAuth 2 redirect from Dropbox server. + // You also need to register this redirect URL on https://www.dropbox.com/developers/apps. + private readonly Uri RedirectUri = new Uri(LoopbackHost + "authorize"); + + // URL to receive access token from JS. + private readonly Uri JSRedirectUri = new Uri(LoopbackHost + "token"); + + + [DllImport("kernel32.dll", ExactSpelling = true)] + private static extern IntPtr GetConsoleWindow(); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetForegroundWindow(IntPtr hWnd); + + [STAThread] + static int Main(string[] args) + { + Console.WriteLine("SimpleTest"); + var instance = new Program(); + try + { + var task = Task.Run((Func>)instance.Run); + + task.Wait(); + + return task.Result; + } + catch (Exception e) + { + Console.WriteLine(e); + throw e; + } + } + + private async Task Run() + { + DropboxCertHelper.InitializeCertPinning(); + + var accessToken = await this.GetAccessToken(); + if (string.IsNullOrEmpty(accessToken)) + { + return 1; + } + + // Specify socket level timeout which decides maximum waiting time when no bytes are + // received by the socket. + var httpClient = new HttpClient(new HttpClientHandler()) + { + // Specify request level timeout which decides maximum time that can be spent on + // download/upload files. + Timeout = TimeSpan.FromMinutes(20) + }; + + try + { + var config = new DropboxClientConfig("SimpleTestApp") + { + HttpClient = httpClient + }; + + var client = new DropboxClient(accessToken, config); + await RunUserTests(client); + + // Tests below are for Dropbox Business endpoints. To run these tests, make sure the ApiKey is for + // a Dropbox Business app and you have an admin account to log in. + + /* + var client = new DropboxTeamClient(accessToken, userAgent: "SimpleTeamTestApp", httpClient: httpClient); + await RunTeamTests(client); + */ + } + catch (HttpException e) + { + Console.WriteLine("Exception reported from RPC layer"); + Console.WriteLine(" Status code: {0}", e.StatusCode); + Console.WriteLine(" Message : {0}", e.Message); + if (e.RequestUri != null) + { + Console.WriteLine(" Request uri: {0}", e.RequestUri); + } + } + + return 0; + } + + /// + /// Run tests for user-level operations. + /// + /// The Dropbox client. + /// An asynchronous task. + private async Task RunUserTests(DropboxClient client) + { + + await GetCurrentAccount(client); + + var path = "/DotNetApi/Help"; + await DeleteFolder(client, path); // Removes items left older from the previous test run. + + var folder = await CreateFolder(client, path); + + var pathInTeamSpace = "/Test"; + await ListFolderInTeamSpace(client, pathInTeamSpace); + + await Upload(client, path, "Test.txt", "This is a text file"); + + await ChunkUpload(client, path, "Binary"); + + ListFolderResult list = await ListFolder(client, path); + + Metadata firstFile = list.Entries.FirstOrDefault(i => i.IsFile); + if (firstFile != null) + { + await Download(client, path, firstFile.AsFile); + } + } + + /// + /// Run tests for team-level operations. + /// + /// The Dropbox client. + /// An asynchronous task. + private async Task RunTeamTests(DropboxTeamClient client) + { + var members = await client.Team.MembersListAsync(); + + var member = members.Members.FirstOrDefault(); + + if (member != null) + { + // A team client can perform action on a team member's behalf. To do this, + // just pass in team member id in to AsMember function which returns a user client. + // This client will operates on this team member's Dropbox. + var userClient = client.AsMember(member.Profile.TeamMemberId); + await RunUserTests(userClient); + } + } + + /// + /// Handles the redirect from Dropbox server. Because we are using token flow, the local + /// http server cannot directly receive the URL fragment. We need to return a HTML page with + /// inline JS which can send URL fragment to local server as URL parameter. + /// + /// The http listener. + /// The + private async Task HandleOAuth2Redirect(HttpListener http) + { + var context = await http.GetContextAsync(); + + // We only care about request to RedirectUri endpoint. + while (context.Request.Url.AbsolutePath != RedirectUri.AbsolutePath) + { + context = await http.GetContextAsync(); + } + + context.Response.ContentType = "text/html"; + + // Respond with a page which runs JS and sends URL fragment as query string + // to TokenRedirectUri. + using (var file = File.OpenRead("index.html")) + { + file.CopyTo(context.Response.OutputStream); + } + + context.Response.OutputStream.Close(); + } + + /// + /// Handle the redirect from JS and process raw redirect URI with fragment to + /// complete the authorization flow. + /// + /// The http listener. + /// The + private async Task HandleJSRedirect(HttpListener http) + { + var context = await http.GetContextAsync(); + + // We only care about request to TokenRedirectUri endpoint. + while (context.Request.Url.AbsolutePath != JSRedirectUri.AbsolutePath) + { + context = await http.GetContextAsync(); + } + + var redirectUri = new Uri(context.Request.QueryString["url_with_fragment"]); + + var result = DropboxOAuth2Helper.ParseTokenFragment(redirectUri); + + return result; + } + + /// + /// Gets the dropbox access token. + /// + /// This fetches the access token from the applications settings, if it is not found there + /// (or if the user chooses to reset the settings) then the UI in is + /// displayed to authorize the user. + /// + /// + /// A valid access token or null. + private async Task GetAccessToken() + { + Console.Write("Reset settings (Y/N) "); + if (Console.ReadKey().Key == ConsoleKey.Y) + { + // Settings.Default.Reset(); + } + Console.WriteLine(); + + + var accessToken = ""; + + if (string.IsNullOrEmpty(accessToken)) + { + try + { + Console.WriteLine("Waiting for credentials."); + var state = Guid.NewGuid().ToString("N"); + var authorizeUri = DropboxOAuth2Helper.GetAuthorizeUri(OAuthResponseType.Token, ApiKey, RedirectUri, state: state); + var http = new HttpListener(); + http.Prefixes.Add(LoopbackHost); + + http.Start(); + + // Use StartInfo to ensure default browser launches. + System.Diagnostics.ProcessStartInfo startInfo = + new System.Diagnostics.ProcessStartInfo(authorizeUri.ToString()); + + System.Diagnostics.Process.Start(startInfo); + + // Handle OAuth redirect and send URL fragment to local server using JS. + await HandleOAuth2Redirect(http); + + // Handle redirect from JS and process OAuth response. + var result = await HandleJSRedirect(http); + + if (result.State != state) + { + // The state in the response doesn't match the state in the request. + return null; + } + + Console.WriteLine("and back..."); + + // Bring console window to the front. + SetForegroundWindow(GetConsoleWindow()); + + accessToken = result.AccessToken; + var uid = result.Uid; + Console.WriteLine("Uid: {0}", uid); + + //Settings.Default.AccessToken = accessToken; + //Settings.Default.Uid = uid; + + //Settings.Default.Save(); + } + catch (Exception e) + { + Console.WriteLine("Error: {0}", e.Message); + return null; + } + } + + return accessToken; + } + + /// + /// Gets information about the currently authorized account. + /// + /// This demonstrates calling a simple rpc style api from the Users namespace. + /// + /// + /// The Dropbox client. + /// An asynchronous task. + private async Task GetCurrentAccount(DropboxClient client) + { + var full = await client.Users.GetCurrentAccountAsync(); + + Console.WriteLine("Account id : {0}", full.AccountId); + Console.WriteLine("Country : {0}", full.Country); + Console.WriteLine("Email : {0}", full.Email); + Console.WriteLine("Is paired : {0}", full.IsPaired ? "Yes" : "No"); + Console.WriteLine("Locale : {0}", full.Locale); + Console.WriteLine("Name"); + Console.WriteLine(" Display : {0}", full.Name.DisplayName); + Console.WriteLine(" Familiar : {0}", full.Name.FamiliarName); + Console.WriteLine(" Given : {0}", full.Name.GivenName); + Console.WriteLine(" Surname : {0}", full.Name.Surname); + Console.WriteLine("Referral link : {0}", full.ReferralLink); + + if (full.Team != null) + { + Console.WriteLine("Team"); + Console.WriteLine(" Id : {0}", full.Team.Id); + Console.WriteLine(" Name : {0}", full.Team.Name); + } + else + { + Console.WriteLine("Team - None"); + } + } + + /// + /// Delete the specified folder including any files within the folder + /// + /// The dropbox client object. + /// The path to the target folder to delete. + /// + private async Task DeleteFolder(DropboxClient client, string path) + { + return await client.Files.DeleteAsync(path); + } + + /// + /// Creates the specified folder. + /// + /// This demonstrates calling an rpc style api in the Files namespace. + /// The path of the folder to create. + /// The Dropbox client. + /// The result from the ListFolderAsync call. + private async Task CreateFolder(DropboxClient client, string path) + { + Console.WriteLine("--- Creating Folder ---"); + var folderArg = new CreateFolderArg(path); + try + { + var folder = await client.Files.CreateFolderV2Async(folderArg); + + Console.WriteLine("Folder: " + path + " created!"); + + return folder.Metadata; + } + catch (ApiException e) + { + if (e.Message.StartsWith("path/conflict/folder")) + { + Console.WriteLine("Folder already exists... Skipping create"); + return null; + } + else + { + throw e; + } + } + } + + /// + /// Lists the items within a folder inside team space. See + /// https://www.dropbox.com/developers/reference/namespace-guide for details about + /// user namespace vs team namespace. + /// + /// The Dropbox client. + /// The path to list. + /// The + private async Task ListFolderInTeamSpace(DropboxClient client, string path) + { + // Fetch root namespace info from user's account info. + var account = await client.Users.GetCurrentAccountAsync(); + + if (!account.RootInfo.IsTeam) + { + Console.WriteLine("This user doesn't belong to a team with shared space."); + } + else + { + try + { + // Point path root to namespace id of team space. + client = client.WithPathRoot(new PathRoot.Root(account.RootInfo.RootNamespaceId)); + await ListFolder(client, path); + } + catch (PathRootException ex) + { + Console.WriteLine( + "The user's root namespace ID has changed to {0}", + ex.ErrorResponse.AsInvalidRoot.Value); + } + } + } + + /// + /// Lists the items within a folder. + /// + /// This demonstrates calling an rpc style api in the Files namespace. + /// The path to list. + /// The Dropbox client. + /// The result from the ListFolderAsync call. + private async Task ListFolder(DropboxClient client, string path) + { + Console.WriteLine("--- Files ---"); + var list = await client.Files.ListFolderAsync(path); + + // show folders then files + foreach (var item in list.Entries.Where(i => i.IsFolder)) + { + Console.WriteLine("D {0}/", item.Name); + } + + foreach (var item in list.Entries.Where(i => i.IsFile)) + { + var file = item.AsFile; + + Console.WriteLine("F{0,8} {1}", + file.Size, + item.Name); + } + + if (list.HasMore) + { + Console.WriteLine(" ..."); + } + return list; + } + + /// + /// Downloads a file. + /// + /// This demonstrates calling a download style api in the Files namespace. + /// The Dropbox client. + /// The folder path in which the file should be found. + /// The file to download within . + /// + private async Task Download(DropboxClient client, string folder, FileMetadata file) + { + Console.WriteLine("Download file..."); + + using (var response = await client.Files.DownloadAsync(folder + "/" + file.Name)) + { + Console.WriteLine("Downloaded {0} Rev {1}", response.Response.Name, response.Response.Rev); + Console.WriteLine("------------------------------"); + Console.WriteLine(await response.GetContentAsStringAsync()); + Console.WriteLine("------------------------------"); + } + } + + /// + /// Uploads given content to a file in Dropbox. + /// + /// The Dropbox client. + /// The folder to upload the file. + /// The name of the file. + /// The file content. + /// + private async Task Upload(DropboxClient client, string folder, string fileName, string fileContent) + { + Console.WriteLine("Upload file..."); + + using (var stream = new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(fileContent))) + { + var response = await client.Files.UploadAsync(folder + "/" + fileName, WriteMode.Overwrite.Instance, body: stream); + + Console.WriteLine("Uploaded Id {0} Rev {1}", response.Id, response.Rev); + } + } + + /// + /// Uploads a big file in chunk. The is very helpful for uploading large file in slow network condition + /// and also enable capability to track upload progerss. + /// + /// The Dropbox client. + /// The folder to upload the file. + /// The name of the file. + /// + private async Task ChunkUpload(DropboxClient client, string folder, string fileName) + { + Console.WriteLine("Chunk upload file..."); + // Chunk size is 128KB. + const int chunkSize = 128 * 1024; + + // Create a random file of 1MB in size. + var fileContent = new byte[1024 * 1024]; + new Random().NextBytes(fileContent); + + using (var stream = new MemoryStream(fileContent)) + { + int numChunks = (int)Math.Ceiling((double)stream.Length / chunkSize); + + byte[] buffer = new byte[chunkSize]; + string sessionId = null; + + for (var idx = 0; idx < numChunks; idx++) + { + Console.WriteLine("Start uploading chunk {0}", idx); + var byteRead = stream.Read(buffer, 0, chunkSize); + + using (MemoryStream memStream = new MemoryStream(buffer, 0, byteRead)) + { + if (idx == 0) + { + var result = await client.Files.UploadSessionStartAsync(body: memStream); + sessionId = result.SessionId; + } + + else + { + UploadSessionCursor cursor = new UploadSessionCursor(sessionId, (ulong)(chunkSize * idx)); + + if (idx == numChunks - 1) + { + await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(folder + "/" + fileName), memStream); + } + + else + { + await client.Files.UploadSessionAppendV2Async(cursor, body: memStream); + } + } + } + } + } + } + + /// + /// List all members in the team. + /// + /// The Dropbox team client. + /// The result from the MembersListAsync call. + private async Task ListTeamMembers(DropboxTeamClient client) + { + var members = await client.Team.MembersListAsync(); + + foreach (var member in members.Members) + { + Console.WriteLine("Member id : {0}", member.Profile.TeamMemberId); + Console.WriteLine("Name : {0}", member.Profile.Name); + Console.WriteLine("Email : {0}", member.Profile.Email); + } + + return members; + } + } +} diff --git a/dropbox-sdk-dotnet/Examples/SimpleTest/Properties/AssemblyInfo.cs b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Properties/AssemblyInfo.cs similarity index 100% rename from dropbox-sdk-dotnet/Examples/SimpleTest/Properties/AssemblyInfo.cs rename to dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Properties/AssemblyInfo.cs diff --git a/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Settings.Designer.cs b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Settings.Designer.cs new file mode 100644 index 0000000000..b39a447292 --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Settings.Designer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SimpleTest { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AccessToken { + get { + return ((string)(this["AccessToken"])); + } + set { + this["AccessToken"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Uid { + get { + return ((string)(this["Uid"])); + } + set { + this["Uid"] = value; + } + } + } +} diff --git a/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Settings.settings b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Settings.settings new file mode 100644 index 0000000000..e9e7b06a15 --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/Settings.settings @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/SimpleTest.csproj b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/SimpleTest.csproj new file mode 100644 index 0000000000..1eaf76f6dc --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/SimpleTest.csproj @@ -0,0 +1,111 @@ + + + + + Debug + AnyCPU + {33A16535-A722-4679-BD9F-A78576988519} + Exe + Properties + SimpleTest + SimpleTest + v4.7.2 + 512 + 8bfaafb2 + ..\ + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + True + True + Settings.settings + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + False + ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll + + + False + ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll + + + False + ..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll + + + ..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + + + False + ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll + + + False + ..\..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll + + + + + + + + {68180b54-4724-4cd1-baa6-ee7bc309797c} + Dropbox.Api + + + + + Always + + + + + + + + + + \ No newline at end of file diff --git a/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/index.html b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/index.html new file mode 100644 index 0000000000..1dda8ec0cf --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/index.html @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/packages.config b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/packages.config new file mode 100644 index 0000000000..47086cb422 --- /dev/null +++ b/dropbox-sdk-dotnet/Examples/SimpleTestDotNetFramework/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file